Railway Operation Simulator  v2.18.0
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand() & random()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 //#include "DisplayUnit.h" included in TrackUnit.h
49 #include "PerfLogUnit.h"
50 #include "Utilities.h"
51 
52 // ---------------------------------------------------------------------------
53 #pragma package(smart_init)
54 
56 
57 // ---------------------------------------------------------------------------
58 
59 int TTrain::NextTrainID = 0; // has to be initialised outside the class
60 
61 // ---------------------------------------------------------------------------
62 
63 TExitInfo::TExitInfo() //default constructor
64 {
65  ServiceReference = " ";
66  RepeatNumber = 0;
67  TimeToExitSecs = -1;
68 }
69 
70 // ---------------------------------------------------------------------------
71 
72 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
73  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
74  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
75  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
76  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
77  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
78 /*
79  Construct a new train with general default values and input values for position and headcode.
80  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
81  This is because trains are kept in a vector and vectors erase elements during internal operations.
82  Deletion is explicit by using a special function. Increment the static class member NextTrainID
83  after setting this train's ID.
84 */
85 
86 {
87  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
88  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
89  AnsiString(TrainModeIn));
90  // AutoControl = true;//all trains start in auto control
91  UpdateCounter = 0;
92  TimeTimeLocArrived = false;
93  Derailed = false;
94  DerailPending = false;
95  Crashed = false;
96  StoppedAtBuffers = false;
97  StoppedAtSignal = false;
98  StoppedAtLocation = false;
99  StoppedAfterSPAD = false;
100  StoppedWithoutPower = false; // new at v2.4.0
101  StoppedForTrainInFront = false;
102  TrainInFront = false; //new at v2.18.0
103  SignallerStoppingFlag = false;
104  SignallerStopped = false;
105  SignallerRemoved = false;
106  NotInService = false;
107  HoldAtLocationInTTMode = false;
108  AllowedToPassRedSignal = false;
109  CallingOnFlag = false;
110  BeingCalledOn = false;
111  DepartureTimeSet = false;
113  TimetableFinished = false;
114  LastActionDelayFlag = false;
115  OneLengthAccelDecel = false;
116  TrainCrashedInto = -1;
118  Plotted = false;
119  TrainGone = false;
120  SPADFlag = false;
121  FrontCodePtr = new Graphics::TBitmap;
122  FrontCodePtr->PixelFormat = pf8bit;
123  FrontCodePtr->Height = 8;
124  FrontCodePtr->Width = 8;
126  FrontCodePtr->Transparent = false;
127  AValue = sqrt(2 * PowerAtRail / Mass);
129  TerminatedMessageSent = false;
130  JoinedOtherTrainFlag = false;
132  FollowOnServiceRef = ""; //added at v2.12.0
133  TreatPassAsTimeLocDeparture = false; //added at v2.12.0
134  StepForwardFlag = false;
136  for(int x = 0; x < 4; x++)
137  {
138  HeadCodeGrPtr[x] = new Graphics::TBitmap;
139  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
140  HeadCodeGrPtr[x]->Height = 8;
141  HeadCodeGrPtr[x]->Width = 8;
143  HeadCodeGrPtr[x]->Transparent = false;
144  }
145  for(int x = 0; x < 4; x++)
146  {
147  BackgroundPtr[x] = new Graphics::TBitmap;
148  BackgroundPtr[x]->PixelFormat = pf8bit;
149  BackgroundPtr[x]->Height = 8;
150  BackgroundPtr[x]->Width = 8;
152  BackgroundPtr[x]->Transparent = false;
153  }
154  for(int x = 0; x < 4; x++)
155  {
157  // set here to ensure have values
158  }
159  for(int x = 0; x < 4; x++)
160  {
161  PlotElement[x] = -1; // marker for not plotted yet
162  }
163  for(int x = 0; x < 3; x++)
164  {
165  OldZoomOutElement[x] = -1; // marker for not plotted yet
166  }
168  NextTrainID++;
169 
170  // new values added to complete initialisation of all TTrain variables
171 
172  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
173  // TrainDataEntryPtr, initialise in AddTrain
175  FrontElementLength = 0;
176  EntrySpeed = 0;
177  ExitSpeedHalf = 0;
178  ExitSpeedFull = 0;
179  MaxExitSpeed = 0;
180  BrakeRate = 0;
181  CoastingBrakeRate = 0.03; //added at v2.18.0
183  FirstHalfMove = true;
184  EntryTime = 0;
185  ExitTimeHalf = 0;
186  ExitTimeFull = 0;
187  ReleaseTime = 0;
188  TRSTime = 0;
189  LastActionTime = 0;
190  Straddle = MidLag;
191  LeadElement = -1;
192  LeadEntryPos = 0;
193  LeadExitPos = 0;
194  MidElement = -1;
195  MidEntryPos = 0;
196  MidExitPos = 0;
197  LagElement = -1;
198  LagEntryPos = 0;
199  LagExitPos = 0;
200  TrainFailed = false; // added at v2.4.0
201  for(int x = 0; x < 4; x++)
202  {
203  HOffset[x] = 0;
204  VOffset[x] = 0;
205  PlotEntryPos[x] = 0;
206  }
207  OpTimeToAct = 60; // default value, new at v2.2.0
208  TimeToExit = -1;
209  ExitPair.first = -1;
210  ExitPair.second = -1;
211  MinsDelayed = 0.0; // new at v2.2.0
212  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
213  FinishJoinLogSent = false;
214  // added at v2.4.0 to prevent repeatdly logging the event
217  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
221  ZeroPowerNoCDTMessage = false;
226  TrainFailurePending = false;
227  SkippedDeparture = false;
228  ActionsSkippedFlag = false;
229  SkipPtrValue = 0;
230  TrainSkippedEvents = 0;
231  DelayedRandMins = 0; //added at v2.13.0
232  NewDelay = 0; //added at v2.13.0
233  CumulativeDelayedRandMinsOneTrain = 0; //added at v2.13.0
234  ActualArrivalTime = TDateTime(0); //added at v2.13.0
235  LastSigPassedFailed = false; //added at v2.13.0
236  Utilities->CallLogPop(648);
237 }
238 
239 // ---------------------------------------------------------------------------
240 
241 void TTrain::DeleteTrain(int Caller)
242 /*
243  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
244  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
245  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
246  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
247  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
248  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
249  No need to delete HeadCodePosition as that just points to existing bitmaps
250 */{
251  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
252  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
253  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
254  if(Display->ZoomOutFlag)
255  {
257  }
258  if(FrontCodePtr == 0)
259  {
260  throw Exception("Error in attempting to delete FrontCodePtr");
261  }
262  delete FrontCodePtr;
263  FrontCodePtr = 0;
264  for(int x = 0; x < 4; x++)
265  {
266  if(BackgroundPtr[x] == 0)
267  {
268  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
269  }
270  delete BackgroundPtr[x];
271  BackgroundPtr[x] = 0;
272  }
273  for(int x = 0; x < 4; x++)
274  {
275  if(HeadCodeGrPtr[x] == 0)
276  {
277  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
278  }
279  delete HeadCodeGrPtr[x];
280  HeadCodeGrPtr[x] = 0;
281  }
282  Utilities->CallLogPop(649);
283 }
284 
285 // ---------------------------------------------------------------------------
286 
288 /*
289  Plots the train starting position on screen. Note that the check for starting on straight points &
290  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
291  ChangeDirection calls this function]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
292  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
293  Set the headcode graphics pointers from the headcode text, then check whether starting at a
294  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
295  for the continuation element. Otherwise set Lead and Mid values,
296 
297  and Lead element value unless
298  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
299  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
300  then check if a train on either Mid or Lag and if so give a warning message and return false so
301  that the calling function can delete the train. Plot the Mid element train values then do similarly
302  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
303  the train. Finally set the Plotted flag and return true.
304 */{
305  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
306  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
307 
309  // PlotStartTime = TrainController->TTClockTime;
310  FirstHalfMove = true;
311 
312  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
313  // 'claim' it for this train to prevent any other waiting trains trying to enter
315  {
316  LagElement = -1; // not to be plotted
317  LagExitPos = 0; // not to be plotted
318  LagEntryPos = 0; // not to be plotted
319  MidElement = -1; // not to be plotted
320  MidExitPos = 0; // not to be plotted
321  MidEntryPos = 0; // not to be plotted
323  LeadExitPos = 1; // will be 1 for continuation entry
324  LeadEntryPos = 0;
325 
327  MaxExitSpeed = StartSpeed; // initial value
329  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
330  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
331  if(EntrySpeed > SpeedLimit)
332  {
333  EntrySpeed = SpeedLimit;
334  }
336  {
338  }
340  // LeadElement is the element to be entered
341 
342  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
343  // can achieve ExitSpeedFull at the half braking rate.
345  {
346  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
347  if(TempEntrySpeed < EntrySpeed)
348  {
349  EntrySpeed = TempEntrySpeed;
351  }
352  }
353  Straddle = MidLag; // only for starting on a continuation
355  // no need to stop gap flashing if start on continuation
356  }
357  else // not starting at a continuation
358  {
359  LagElement = -1;
360  LagEntryPos = 0;
361  LagExitPos = 0;
368 
370  MaxExitSpeed = StartSpeed; // initial value
372  bool TempDerail = false; // dummy
373  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
375  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
376  {
377  StoppedWithoutPower = true;
378  }
379  // facing buffers check - ignore starting speed if start facing buffers
380  StoppedAtBuffers = false;
381  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
384  {
385  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
386  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
387  EntrySpeed = 0;
388  ExitSpeedHalf = 0;
389  ExitSpeedFull = 0;
390  MaxExitSpeed = 0;
391  // SetTrainMovementValues not called so set this here
392  BrakeRate = 0;
395  StoppedAtSignal = false;
396  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
397  // signal check is an 'else'
398  if(!StoppedAtLocation)
399  {
400  StoppedAtBuffers = true; // stopped at location takes precedence
401  }
402  }
403 
404  // facing continuation check - don't allow to stop even if no power
406  {
407  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
408  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
412  BrakeRate = 0;
413  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
414  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
415  }
416 
417  // Signal check
418  else if((NextElementPosition > -1) && (NextEntryPos > -1))
419  // condition check added as precaution after SloughIECC error reported by James U
420  {
421  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
422  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
423  {
424  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
425  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
426  EntrySpeed = 0;
427  ExitSpeedHalf = 0;
428  ExitSpeedFull = 0;
429  MaxExitSpeed = 0;
430  BrakeRate = 0;
433  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
434  {
435  StoppedAtSignal = true;
437  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
438  }
440  {
441  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass stop signal' is offered in popup menu rather than move
442  // forwards, but don't change the background colour so still shows as stopped at location
443  StoppedAtSignal = true;
444  }
445  }
446  else
447  {
448  StoppedAtSignal = false;
449  if(NextEntryPos > 1)
450  {
451  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
452  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
453  }
454  else
455  {
456  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
457  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
458  }
459  if(EntrySpeed > SpeedLimit)
460  {
461  EntrySpeed = SpeedLimit;
462  }
464  {
466  }
468  TDateTime TestTime = TrainController->TTClockTime; // test
469  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
470  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
471  // NextElement is the element to be entered
472 
473  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
474  // can achieve ExitSpeedFull at the half braking rate.
476  {
477  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
478  // half braking
479  if(TempEntrySpeed < EntrySpeed)
480  {
481  EntrySpeed = TempEntrySpeed;
482  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
483  }
484  }
485  }
486  }
488  {
489  throw Exception("Error, LeadElement Exit Connection is NotSet");
490  }
491  }
492  if(MidElement > -1) // will be -1 if start on continuation
493  {
494  Straddle = LeadMid;
498  {
499  for(int x = 0; x < 4; x++)
500  {
501  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
502  }
503  }
504  else
505  {
506  for(int x = 0; x < 4; x++)
507  {
509  }
510  }
511  if(TrainMode == Timetable)
512  {
514  }
515  else
516  {
518  }
520  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
521 
524 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
525  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
526  {
527  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
528  Utilities->CallLogPop(651);
529  return false;
530  }
531 */
536  PlotTrainGraphic(8, 0, Display);
537  PlotTrainGraphic(9, 1, Display);
538 
541 
542  // pick up background bitmaps [2] & [3]
543 
546 
547  PlotElement[2] = MidElement;
549  PlotElement[3] = MidElement;
551  PlotTrainGraphic(10, 2, Display);
552  PlotTrainGraphic(11, 3, Display);
553  // Plotted = true; set in PlotTrainGraphic
554  }
555  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
556  Utilities->CallLogPop(652);
557 }
558 
559 // ---------------------------------------------------------------------------
560 void TTrain::UnplotTrain(int Caller)
561 {
562  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
563  if(!Plotted)
564  {
565  return;
566  }
567  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
568 
569  if(Straddle == MidLag)
570  {
571  if(MidElement > -1)
572  {
577  // to force plot of locked route marker, needed once only for the element
578  }
579  if(LagElement > -1)
580  {
585  // to force plot of locked route marker, needed once only for the element
586  }
587  }
588  else if(Straddle == LeadMidLag)
589  {
590  if(LeadElement > -1)
591  {
594  // to force plot of locked route marker, needed once only for the element
595  }
596  if(MidElement > -1)
597  {
602  // to force plot of locked route marker, needed once only for the element
603  }
604  if(LagElement > -1)
605  {
608  // to force plot of locked route marker, needed once only for the element
609  }
610  }
611  else if(Straddle == LeadMid)
612  {
613  if(LeadElement > -1)
614  {
619  // to force plot of locked route marker, needed once only for the element
620  }
621  if(MidElement > -1)
622  {
627  // to force plot of locked route marker, needed once only for the element
628  }
629  }
630  if(LeadElement > -1)
631  {
633  }
634  if(MidElement > -1)
635  {
637  }
638  if(LagElement > -1)
639  {
641  }
642  Plotted = false;
644  Display->Update();
645  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
646  // resurrected when Update() dropped from PlotOutput etc
647  Utilities->CallLogPop(653);
648 }
649 
650 // ----------------------------------------------------------------------------
651 
652 void TTrain::UpdateTrain(int Caller)
653 /*
654  Note: Some changes made since comments written
655 
656  Brief:
657  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
658  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
659  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
660  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
661  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
662  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
663  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
664  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
665  changed to MidLag within the function and all elements moved down one, old Mid becomes
666  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
667  incremented to reflect the position the train now occupies.
668 
669  Detail:
670  Set TrainFailurePending if all conditions met
671  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
672  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
673  and return.
674  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
675  If there's a LagElement (there will be but include check for good practice - next
676  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
677  train fully on offending point - Derail set and DerailPanding reset, train background
678  colour changed (note that BackgroundColour is a property of the train itself) then return.
679  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
680  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
681  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
682  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
683  if LeadElement is a fouled trailing point.
684  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
685  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
686  replotting the last background segment and checking whether the element is a bridge or crossover with the other
687  track in a route, in which case the route colour is replotted.
688  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
689  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
690  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
691  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
692  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
693  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
694  train can be deleted by the calling function, and the function returns.
695  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
696  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
697  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
698  basic red aspect.
699 
700  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
701  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
702  regardless of direction, and with the correct front code colour.
703 
704  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
705  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
706  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
707  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
708  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
709  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
710  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
711 
712  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
713  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
714  changed similarly. The function then returns.
715 
716  If Crashed is not set then Straddle is incremented and the function returns.
717 */
718 
719 {
720  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
721  UpdateCounter++;
722  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
723  if(UpdateCounter >= 100)
724  {
725  UpdateCounter = 0;
726  }
727  int RandRange = (TrainController->MTBFHours * 3600) / 53;
728 
729  // MTBFHours is in timetable clock hours, min value is 1 & max value is 9,999 (integer values on input)
730  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
731  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
732  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
733  // RandomFailureCounter value is fixed for a full cycle of train updates so this
734  // makes sure there's no bunching of failures as there is for a fixed comparison number
735  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
736  // gives a random number between 0 and 16384 (defined as RAND_MAX in stdlib.h)
737  {
738  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
739  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
740  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
741  // don't fail if:
742  // (a) on a continuation (entering or leaving);
743  // (b) already failed;
744  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
745  // (d) train terminated;
746  // (e) crashed or derailed; or
747  // (f) under signaller control and stopped.
748  // (g) TreatPassAsTimeLocDeparture is true //added at v2.12.0
749  {
750  if((random(RandRange) == 0) && !TreatPassAsTimeLocDeparture)
751  // max value for RandRange is over 2x10^9
752  {
753  // here if failure due
754  TrainFailurePending = true;
755  // the failure occurs when PlotElements set to proper Lead & Mid Elements
756  }
757  }
758  }
759 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
760  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
761  {
762  StoppedWithoutPower = true;
763  }
764 */
765 // float TimeToExit; //added at v2.10.0 Removed these so original values retained - used when train on continuation
766 // THVShortPair ExitPair; //added at v2.10.0
767  int LockedVectorNumber;
768  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
769  // default values - these needed for route checker below
770  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
771 
773  {
775  }
776  if(Crashed || Derailed)
777  {
779  {
780  PlotTrain(7, Display);
781  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
782  Display->Update();
783  }
784  OpTimeToAct = 0.0;
785  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
786  Utilities->CallLogPop(1017);
787  return; // no further action, user has to remove or work around
788  }
789 
791  {
793  }
795  {
797  }
798  if(StoppedWithoutPower && (TrainMode == Signaller)) //added at v2.13.2 as this condition not covered - was shown as normal
799  {
801  }
803  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
804  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
805  // to move & then stop again at the same station
806  {
807  TimeTimeLocArrived = false;
808  }
809  if(!Stopped() && !SPADFlag && !TrainFailed)
810  {
812  }
813  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
814  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
815 /* old version where force a stop at buffers regardless of speed
816  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
817  else StoppedAtBuffers = false;
818 */
819 
820  // new version where crash if run into buffers
821  if(!Crashed)
822  {
823  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
824  {
825  if(ExitSpeedFull > 1)
826  {
827  Crashed = true;
831  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
832  // no need for missed action logs - will be sent when train removed
833  StoppedAtBuffers = false;
834  }
836  // stopped at location & stopped without power take precedence
837  {
838  StoppedAtBuffers = true;
839  }
840  else
841  {
842  StoppedAtBuffers = false;
843  }
844  }
845  else
846  {
847  StoppedAtBuffers = false;
848  }
849  }
850  else
851  {
852  StoppedAtBuffers = false;
853  }
854  // if crashed don't want stopped at buffers set
855 
856  // also crash if run into a level crossing that is changing or has barriers up
857  if(!Crashed)
858  {
859  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
860  {
861  int H = Track->TrackElementAt(873, LeadElement).HLoc;
862  int V = Track->TrackElementAt(874, LeadElement).VLoc;
863  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
864  {
865  Crashed = true;
869  // no need for missed action logs - will be sent when train removed
870  }
871  }
872  }
874  {
876  }
877  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
879  //if Command == "" then either TimeLoc or TimeTimeLoc so don't hold, and added last part at v2.12.0 so don't hold if have both command == pas and Treat... flag
880  {
881  HoldAtLocationInTTMode = true;
882  }
883  else if(TrainMode == Timetable)
884  {
885  HoldAtLocationInTTMode = false;
886  }
887  // in Signaller mode HoldAtLocationInTTMode not changed
888 
889  // check if departure pending & set times unless already set
890  if(TrainMode == Timetable)
891  {
893  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
894  {
895  if((ActionVectorEntryPtr->Command != "pas") && (ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) && (ActualArrivalTime > TDateTime(0)))
896  {
897  AnsiString ReasonArray[24] = {"a driver is awaited","a guard is awaited","of a medical emergency","of a technical problem","of a security issue",
898  "of a safety issue","of a disturbance","a train crew member has been taken ill","the driver has been taken ill","the guard has been taken ill",
899  "a report has been received concerning safety","a shoe has been lost under the train","of a reported theft",
900  "of an incident involving an animal","some luggage has been lost under the train","a minor repair is needed","a suspicious object has to be dealt with safely",
901  "a door is stuck open","additional stock has to be attached","a security alert","of a train fault","of an operating incident","safety checks are required",
902  "of a shortage of on train crew"};
903  //(ActionVectorEntryPtr->Command != "pas") added at v2.13.0 to rule out passes, though probably not needed
904  //(ActualArrivalTime > TDateTime(0)) added at v2.13.0 to ensure that it has been set and to dismiss trains that are present
905  //at start or have no departure time set.
906  NewDelay = 0; //section relating to random delays added at v2.13.0
907  TDateTime TimetableReleaseTime = TrainController->GetRepeatTime(0, ActionVectorEntryPtr->DepartureTime, RepeatNumber, IncrementalMinutes); //Timetable value
908  TDateTime DwellTime = TimetableReleaseTime - ActualArrivalTime; //Timetable value
909  if(DwellTime < TDateTime(30.0 / 86400))
910  {
911  DwellTime = TDateTime(30.0 / 86400);
912  }
913  int randval = random(10000);
914  if(randval != 0) //if randval == 0 or DelayMode == Nil then NewDelay will be 0 as set above
915  {
916  if(Utilities->DelayMode == Minor)
917  {
918  if(randval < Utilities->MinorDelayCutoff)
919  {
920  NewDelay = Utilities->MinorDelayFactor * log(Utilities->MinorDelayCutoff/randval);//minutes (confusingly log in C++Builder gives the natural logarithm)
921  }
922  }
923  else if(Utilities->DelayMode == Moderate)
924  {
925  if(randval < Utilities->ModerateDelayCutoff)
926  {
928  }
929  }
930  else if(Utilities->DelayMode == Major)
931  {
932  if(randval < Utilities->MajorDelayCutoff)
933  {
935  }
936  }
937  }
938 //NewDelay = 25; //test
939  if(double(TrainController->TTClockTime) <= (Utilities->LastDelayTTClockTime + 5.0/1440.0)) //if within 5 mins of last delay for any train
940  { //then don't delay. Added at v2.13.0
941  NewDelay = 0;
942  }
943  if(NewDelay < 1)
944  {
945  NewDelay = 0;
946  }
947  if(NewDelay < double(DwellTime) * 1440) //if less than scheduled dwell time then no additional delay
948  {
949  NewDelay = 0;
950  }
951  else
952  {
953  NewDelay -= double(DwellTime) * 1440;//reduce delay by dwell time
954  }
955  if(DelayedRandMins > 0)
956  {
957  DelayedRandMins -= double(DwellTime) * 1440;//reduce knock-on random delay by dwell time
958  }
959  if(DelayedRandMins < 0)
960  {
961  DelayedRandMins = 0;//can't be less than zero
962  }
964  {
965  NewDelay -= DelayedRandMins; //NewDelay is the additional delay over and above the existing knock-on delay (from earlier random delays)
966  //the formula above already includes knock-on effects
967  DelayedRandMins += NewDelay; //the new total delay, knock-on + additional
968 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //don't add here, add when depart, else this value can be > late mins
969  }
970  else
971  {
972  NewDelay = 0;
973 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //as above
974  }
975  ReleaseTime = LastActionTime + TDateTime(NewDelay / 1440); //earliest possible release time
976  if(NewDelay < 0.5) //less than the 30 secs min interval
977  {
978  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
979  }
980  if(ReleaseTime < TimetableReleaseTime)
981  {
982  ReleaseTime = TimetableReleaseTime; //back to correct time
983  NewDelay = 0;
984  DelayedRandMins = 0;
985  }
986  if(DelayedRandMins > double(ReleaseTime - TimetableReleaseTime) * 1440)
987  {
988  DelayedRandMins = double(ReleaseTime - TimetableReleaseTime) * 1440; //reduce this if time has been made up
989  }
990 
991  if(DelayedRandMins < NewDelay) //may be if reduced above, but if so need to reduce NewDelay also
992  {
994  }
995  //may be possible to simplify all the above but as it seems to work ok leave as is
996  if(int(NewDelay) > 0) //additional delay over and above knock-on effects from earlier random delays
997  {
999  if(int(NewDelay) == 1)
1000  {
1001  Display->WarningLog(12, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " delayed at " +
1002  ActionVectorEntryPtr->LocationName + " by 1 minute");
1003  PerfLogForm->PerformanceLog(18, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: " + HeadCode + " delayed at " +
1004  ActionVectorEntryPtr->LocationName + " by 1 minute because of a minor technical issue");
1005  TrainController->StopTTClockMessage(140, HeadCode + " delayed at " +
1006  ActionVectorEntryPtr->LocationName + " by 1 minute because of a minor technical issue");
1007  }
1008  else
1009  {
1010  Display->WarningLog(11, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " delayed at " +
1011  ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) + " minutes");
1012  if(NewDelay >= 10) //give variable reasons for >= 10 mins
1013  {
1014  int randval2 = rand() % 24; //24 reasons
1015  AnsiString Reason = ReasonArray[randval2];
1017  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1018  " minutes because " + Reason);
1019  TrainController->StopTTClockMessage(141, HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1020  " minutes because " + Reason);
1021  }
1022  else
1023  {
1025  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1026  " minutes because of a minor problem");
1027  TrainController->StopTTClockMessage(142, HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1028  " minutes because of a minor problem");
1029  }
1030  }
1031  }
1032  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1033  ActualArrivalTime = TDateTime(0); //only run through this section once per arrival
1034  DepartureTimeSet = true;
1035  }
1036  else if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) //as was, for trains that don't have an errival time set
1037  {//if have skipped to a new service then DepartureTime will be set (in above segement when earlier train arrived)
1038  //but ArrivalTime won't be set as it is reset to 0 at end of above segement when earlier train arrived, so this segement
1039  //will run without any random delays which might cause additional complications from mixing modifications and best avoided.
1040  NewDelay = 0;
1041  DelayedRandMins = 0;
1043  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1044  {
1045  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1046  }
1047  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1048  DepartureTimeSet = true;
1049  }
1050  else if((ActionVectorEntryPtr->Command == "pas") && TreatPassAsTimeLocDeparture) //new segment at v2.12.0 to treat a pass as a departure
1051  {//for when skip to a new service at a pass location. As above this also avoids any random delays, and will avoid above segment because
1052  //departure time isn't set - it's an event time. Again random delays in this situation might cause additional complications
1053  //from mixing modifications so best avoided.
1054  NewDelay = 0;
1055  DelayedRandMins = 0;
1057  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1058  {
1059  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1060  }
1061  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1062  DepartureTimeSet = true;
1063  }
1064  }
1065  }
1066  if(TrainController->OpTimeToActUpdateCounter == 0)// && TrainController->OpActionPanelVisible) removed last condition so always calc TimeToExit
1067  {
1068  OpTimeToAct = CalcTimeToAct(0, TimeToExit, ExitPair); // called after ReleaseTime set
1069 // this->TimeToExit = TimeToExit; don't need these as values updated directly
1070 // this->ExitPair = ExitPair;
1071  // calculate every 1 sec (in real time, not timetable time) for all trains
1072  }
1073  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
1074  if(TrainMode == Timetable)
1075  {
1076  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
1077  {
1078  RemainHereLogNotSent = true;
1079  }
1081  {
1082  // ignore TimeLoc & TTLoc departures
1083  // Action logs given in functions
1085  LastActionTime + TDateTime(30.0 / 86400)))
1086  {
1087  if(ActionVectorEntryPtr->Command == "fsp")
1088  {
1089  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to
1090  //'this' train. Next clock cycle will deal with any required changes
1091  FrontTrainSplit(0);
1092  if(TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
1093  { //this is checked before each return
1094  TrainHasFailed(0);
1095  }
1096  Utilities->CallLogPop(2041);
1097  return;
1098  }
1099  else if(ActionVectorEntryPtr->Command == "rsp")
1100  {
1101  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to
1102  //'this' train. Next clock cycle will deal with any required changes
1103  RearTrainSplit(0);
1104  if(TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
1105  {
1106  TrainHasFailed(1);
1107  }
1108  Utilities->CallLogPop(2042);
1109  return;
1110  }
1111  else if(ActionVectorEntryPtr->Command == "Fjo")
1112  {
1113  FinishJoin(0);
1114  }
1115  else if(ActionVectorEntryPtr->Command == "jbo")
1116  {
1117  JoinedBy(0);
1118  }
1119  else if(ActionVectorEntryPtr->Command == "cdt")
1120  {
1121  ChangeTrainDirection(0, false);
1122  }
1123  else if(ActionVectorEntryPtr->Command == "dsc")
1124  {
1125  Description = ActionVectorEntryPtr->NewDescription; //changed at v2.16.1
1129  }
1130  else if(ActionVectorEntryPtr->Command == "Fns")
1131  {
1132  NewTrainService(0, false);
1133  }
1134  else if(ActionVectorEntryPtr->Command == "Frh")
1135  {
1136  RemainHere(0);
1137  }
1138  else if(ActionVectorEntryPtr->Command == "Fer")
1139  {
1140  TimetableFinished = true;
1141  }
1142  // other aspects of 'Fer' dealt with in TTrain::HasTrainGone()
1143  else if(ActionVectorEntryPtr->Command == "F-nshs")
1144  {
1146  }
1147  else if(ActionVectorEntryPtr->Command == "Frh-sh")
1148  {
1149  RepeatShuttleOrRemainHere(0, false);
1150  }
1151  else if(ActionVectorEntryPtr->Command == "Fns-sh")
1152  {
1154  }
1155 /*
1156  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
1157  shuttle headcode (no train creation)
1158  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1159  remain here
1160  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1161  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
1162 */
1163  }
1164  }
1165  else
1166  {
1168  {
1170  }
1171  }
1172  }
1173  if(TrainMode == Timetable)
1174  {
1175  if(StoppedAtBuffers)
1176  {
1177  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
1178  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
1179  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
1180  if(BufferLocation == "")
1181  {
1183  }
1184  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
1185  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
1186  {
1190  {
1192  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1193  // Drop missed actions so user can still use sig mode to get back on track
1195  }
1196  if(TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
1197  {
1199  TrainHasFailed(2);
1200  }
1201  Utilities->CallLogPop(1020);
1202  return;
1203  }
1204  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !RevisedStoppedAtLoc() && (TrainController->TTClockTime >
1205  ReleaseTime))
1206  {
1209  {
1212  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1213  // Drop missed actions so user can still use sig mode to get back on track
1215  }
1216  if(TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
1217  {
1219  TrainHasFailed(3);
1220  }
1221  Utilities->CallLogPop(1397);
1222  return;
1223  }
1224  }
1225  else
1226  {
1228  }
1229  }
1230  else
1231  {
1233  }
1234  if(TrainMode == Timetable)
1235  {
1237  {
1239  }
1241  {
1243  }
1244  }
1245  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1246  // restart after stopped for train in front
1247  int NextElementPosition, NextEntryPos;
1248 
1249  if(LeadElement > -1) // if an exit continuation then not set
1250  {
1251  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1252  {
1254  }
1255  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1256  {
1257  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1258  {
1259  LeadExitPos = 1;
1260  }
1261  else
1262  {
1263  LeadExitPos = 3;
1264  }
1265  }
1266  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1267  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1268  }
1269  else
1270  {
1271  NextElementPosition = -1;
1272  NextEntryPos = -1;
1273  }
1274  if((NextElementPosition > -1) && (NextEntryPos > -1))
1275  // may be buffers or continuation so need this check
1276  {
1277 /*
1278  Check whether calling-on conditions met:-
1279  a) approaching train has stopped at a signal but not at a location;
1280  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1281  change of direction (cdt), remaining here (Frh), or under signaller control);
1282  c) at least 1 platform available for the approaching train;
1283  d) points (if any) set for direct route into platform;
1284  e) approaching train is to stop at station;
1285  f) no more facing signals between train and platform;
1286  g) [dropped g]
1287  h) train in front preventing route being set far enough to release stop signal;
1288  i) train in front not exiting at continuation;
1289  j) signal must be within 4km of the stop platform;
1290  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily];
1291  l) no existing route conflicts with the route into the platform; and
1292  m) not failed or without power (these added at v2.10.0)
1293  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1294  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1295 */
1296  if(TrainMode == Timetable)
1297  {
1298  if(CallingOnAllowed(0)) //returns false if failed or no power (modified afer v2.9.2)
1299  {
1300  CallingOnFlag = true;
1301  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1302  }
1303  else
1304  {
1305  if(CallingOnFlag) //TrainHasFailed sets this flag to false (at v2.10.0)
1306  {
1307  if(!TrainFailed) //shouldn't be needed but include for safety at v2.10.0
1308  {
1310  }
1311  }
1312  CallingOnFlag = false;
1313  }
1314  }
1315  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !RevisedStoppedAtLoc())
1316  {
1317  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1318  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1319  // sets StoppedAtSignal again & train doesn't move
1320  StoppedAtSignal = false;
1321  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1322  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1323  // LeadMidLag and front of train was on LeadElement (after the current move)
1325  EntrySpeed = 0;
1327  FirstHalfMove = true;
1328  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1329  // NextElement is the element to be entered
1330  }
1331  if((LeadElement > -1) && (LeadExitPos > -1))//this section added at v2.18.0
1332  {
1333  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1334  if(NextPos > -1)
1335  {
1336  int NextEntryPos = Track->TrackElementAt(1674, LeadElement).ConnLinkPos[LeadExitPos];
1337  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1338  // true if another train on NextEntryPos track whether bridge or not
1339  {
1340  TrainInFront = true;
1341  }
1342  else
1343  {
1344  TrainInFront = false;
1345  }
1346  }
1347  }
1349  {
1350  if(ClearToNextSignal(0))
1351  {
1352  StoppedForTrainInFront = false;
1353  TrainInFront = false;
1354  BeingCalledOn = false;
1355  EntrySpeed = 0;
1357  FirstHalfMove = true;
1358  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1359  }
1360  }
1361  }
1362  if(Stopped() && TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
1363  {
1364  TrainHasFailed(4);
1365  Utilities->CallLogPop(1097);
1366  return;
1367  }
1368  if((Straddle == MidLag) && (LeadElement != -1))
1369  // later check only for Straddle == LeadMid, so need this check here for initial train start
1370  {
1372  }
1373 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1374  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1375  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1376  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1377  which could be when start as Snt.
1378  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1379  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1380  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1381  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1382  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1383  reached.
1384  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1385  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1386  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1387  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1388  sending a message to the performancelog.
1389 */
1390 
1391  if(TrainMode == Timetable)
1392  {
1394  {
1395 // if(BeingCalledOn) //dropped when added TrainInFront at v2.18.0
1396 // {
1397 // TrainInFront = true;
1398 // }
1400  {
1402  }
1403  else //added at v2.14.0 as if a train ready to depart (pink b'gnd) taken under sig control then restored to tt control b'gnd stayed pink,
1404  { //even though release time now 30 seconds after tt control restored
1406  }
1408  {
1409  // value updated at every scheduled departure & arrival
1411  AnsiString StationName;
1413  {
1415  }
1417  {
1419  }
1420  else
1421  {
1422  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1423  }
1424  EntrySpeed = 0;
1425  bool Derail; //not used
1427  int NextElementPosition = Track->TrackElementAt(199, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(8, LeadElement, LeadEntryPos, Derail)];
1429  FirstHalfMove = true; //above changed at 2.18.0 from GetNonPoints... to GetAnyElement... as had wrong
1430  StoppedAtLocation = false; //element and link found with non-station names on points as can now stop on points
1431 
1432  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1433  {
1434  StoppedWithoutPower = true;
1435  }
1436  if((NextElementPosition > -1) && (NextEntryPos > -1))
1437  // condition check added for SloughIECC error reported by James U
1438  {
1439  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1440  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1441  {
1442  StoppedAtSignal = true;
1443  if(!StoppedWithoutPower)
1444  // if stopped without power just keep existing background colour
1445  {
1447  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1448  }
1449  }
1450  }
1452  {
1453  TimeTimeLocArrived = false;
1454  LogAction(27, HeadCode, "", Depart, StationName, "", ActionVectorEntryPtr->DepartureTime, false);
1455  // no warning for TimeTimeLoc departure
1456  }
1457  else if(TreatPassAsTimeLocDeparture) //added at v2.12.0 so late/early/on time mins recorded accurately
1458  {
1459  LogAction(36, HeadCode, "", Depart, StationName, "", ActionVectorEntryPtr->EventTime, ActionVectorEntryPtr->Warning); //EventTime because the real event is a pass
1460  }
1461  else //must be TimeLoc departure
1462  {
1464  }
1465  TreatPassAsTimeLocDeparture = false; //added at v2.12.0, reset after train departs
1466  DepartureTimeSet = false;
1467  // no need to set LastActionTime for a departure
1468  //deal here with departure pointer change, increment if SkippedDeparture
1469  CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //only add these after late mins added (in LogAction)
1470 
1471  if(SkippedDeparture)
1472  {
1475  TrainSkippedEvents = 0;
1476  SkippedDeparture = false;
1477  SkipPtrValue = 0;
1478  ActionsSkippedFlag = false;
1479  }
1480  else
1481  {
1483  }
1484  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1485  // note
1486 /*
1487  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1488  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1489  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1490  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1491  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1492  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1493  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1494 */
1496  {
1497  StoppedAtBuffers = true;
1498  }
1499  else if(!StoppedWithoutPower)
1500  // if buffers or no power, don't set values
1501  {
1503  {
1504  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1505  // NextElement is the element to be entered
1506  }
1507  else
1508  {
1510  // use LeadElement for an exit continuation
1511  }
1512  }
1513  }
1514  }
1515  }
1516  if(Straddle == LeadMidLag) //train on a half element
1517  {
1519  {
1520  Utilities->CallLogPop(654);
1521  return;
1522  }
1523  }
1524  else //train fully on 2 elements
1525  {
1527  {
1528  Utilities->CallLogPop(655);
1529  return;
1530  }
1531  }
1532  if((LeadElement > -1) && (MidElement > -1))
1533  {
1535  {
1536  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1537  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1538  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1539  SignallerStoppingFlag = false;
1540  StepForwardFlag = false;
1541  }
1542  }
1543  if(Stopped())
1544  // this is what prevents another movement if the train is stopped
1545  {
1546  if(TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
1547  {
1548  TrainHasFailed(5);
1549  }
1550  BrakeRate = 0;
1551  Utilities->CallLogPop(656);
1552  return;
1553  }
1554 
1555  // HERE WHEN READY FOR NEXT MOVE
1556 
1557  //added at v2.10.0 to set SPADFlag if red signal immediately ahead (as it will be if in a locked route)
1558  //check if due to run past a red signal & if so set SPADFlag (SetTrainMovementValues & its SPAD check only called when arrive fully on 2 elements)
1559  if(Straddle == LeadMid) //fully on 2 elements
1560  {
1561  if(LeadElement > -1)
1562  {
1563  if(Track->TrackElementAt(1402, LeadElement).Conn[LeadExitPos] > -1)
1564  {
1566  if(TIF.TrackType == SignalPost)
1567  {
1568  int TIFEntryPos = Track->TrackElementAt(1405, LeadElement).ConnLinkPos[LeadExitPos];
1569  int TIFExitPos = 0;
1570  if(TIFEntryPos == 0)
1571  {
1572  TIFExitPos = 1;
1573  }
1574  if((TIF.Config[TIFExitPos] == Signal) && TIF.Attribute == 0 && (ExitSpeedHalf > 1) && !AllowedToPassRedSignal && !TIF.CallingOnSet) //use ExitSpeedHalf as may have been stopped at signal so entryspeed is 0
1576  {
1577  SPADFlag = true; // user has to intervene to reset & restart after spad
1578  }
1579  }
1580  }
1581  }
1582  }
1583 
1584  // check for train in front & if so stop at next access (when train fully on element next to train)
1585  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1586  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1587  // variable TrainInFrontInSignallerModeFlag
1588  {
1589  if((LeadElement > -1) && (LeadExitPos > -1))
1590  {
1591  int NextPos = Track->TrackElementAt(1672, LeadElement).Conn[LeadExitPos];
1592  int NextEntryPos = Track->TrackElementAt(1675, LeadElement).ConnLinkPos[LeadExitPos];
1593  if(Track->OtherTrainOnTrack(16, NextPos, NextEntryPos, TrainID))
1594  // true if another train on NextEntryPos track whether bridge or not
1595  {
1596  TrainInFront = true;
1597  }
1598  else
1599  {
1600  TrainInFront = false;
1601  }
1602  }
1603  }
1604  if((Straddle == LeadMid) && SPADFlag)
1605  // give message + plot background when ready to move half past the signal
1606  {
1607  if(NextElementPosition > -1)
1608  {
1609  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1610  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1611  {
1612  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1614  // if goes past 2 signals then give message twice
1616  }
1617  }
1618  }
1619  if(Straddle == LeadMidLag)
1620  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1621  {
1622  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1623  if(SPADFlag)
1624  {
1625  if(ExitSpeedFull == 0)
1626  {
1627  StoppedAfterSPAD = true;
1628  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1629  }
1630  }
1632  {
1633  if(ExitSpeedFull == 0)
1634  {
1635  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1636  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1637  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1638  // is sent at the right time and once only.
1639  SignallerStopped = true;
1640  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1641  StepForwardFlag = false;
1642  SignallerStoppingFlag = false;
1643  TTrackElement TE;
1644  AnsiString Loc = "";
1645  bool LocNamed = false;
1646  if(LeadElement > -1)
1647  {
1648  TE = Track->TrackElementAt(782, LeadElement);
1649  if(TE.ActiveTrackElementName != "")
1650  {
1651  Loc = TE.ActiveTrackElementName;
1652  LocNamed = true;
1653  }
1654  else
1655  {
1656  Loc = "track element " + TE.ElementID;
1657  }
1658  }
1659  if((MidElement > -1) && !LocNamed)
1660  {
1661  TE = Track->TrackElementAt(783, MidElement);
1662  if(TE.ActiveTrackElementName != "")
1663  {
1664  Loc = TE.ActiveTrackElementName;
1665  LocNamed = true;
1666  }
1667  else if(Loc == "")
1668  {
1669  Loc = "track element " + TE.ElementID;
1670  }
1671  }
1672  if(Loc == "")
1673  {
1674  Loc = "outside railway";
1675  // must have stopped after left at a continuation (because both lead & mid == -1)
1676  }
1677  else
1678  {
1679  Loc = "at " + Loc;
1680  }
1681  LogAction(30, HeadCode, "", SignallerStop, Loc, "", TrainController->TTClockTime, false); // false for warning
1682  }
1683  }
1684  if(LeadElement > -1) // if an exit continuation then not set
1685  {
1686  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1687  {
1689  }
1690  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1691  {
1692  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1693  {
1694  LeadExitPos = 1;
1695  }
1696  else
1697  {
1698  LeadExitPos = 3;
1699  }
1700  }
1701  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1702  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1703  }
1704  else
1705  {
1706  NextElementPosition = -1;
1707  NextEntryPos = -1;
1708  }
1711  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1712 
1713  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1714  {
1715  StoppedWithoutPower = true;
1716  }
1717  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1718  // may be buffers or continuation. SPADFlag added at v2.1.0
1719  // so don't override the SPAD colour & don't set StoppedAtSignal
1720  {
1721  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1722  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !RevisedStoppedAtLoc())
1723  {
1724  StoppedAtSignal = true;
1725  if(!StoppedWithoutPower)
1726  // leave background as is if no power, but set StoppedAtSignal
1727  {
1729  }
1730  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1731  }
1732  }
1733  if(!Stopped())
1734  {
1735  if((NextElementPosition > -1) && (NextEntryPos > -1))
1736  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1737  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1738  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1739  // function for fuller explanation
1740  {
1741  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1742  // NextElement is the element to be entered
1743  }
1744  // follow the continuation exits:-
1745  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1746  {
1748  // Use LeadElement for calcs if lead is a continuation
1749  }
1750  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1751  {
1753  // Use MidElement for calcs if mid is a continuation
1754  }
1755  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1756  {
1758  // Use LagElement for calcs if lag is a continuation
1759  }
1760  }
1761  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1762  if((AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute))
1763  // Trains may not be in a route
1764  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1765  {
1766  // NB if LeadElement == -1 then the above test returns NoRoute
1767  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1768  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1769  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1770  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1771  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1772  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1773  FirstPair.second).GetELink() == TempELink))
1774  {
1775  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1776  }
1777  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1778  SecondPair.second).GetELink() == TempELink))
1779  {
1780  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1781  }
1782  }
1783  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1784  // Trains may not be in a route
1785  {
1786  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1787  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1788  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1789  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1790  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1791  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1792  FirstPair.second).GetELink() == TempELink))
1793  {
1794  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1795  }
1796  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1797  SecondPair.second).GetELink() == TempELink))
1798  {
1799  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1800  }
1801  }
1802  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1803  // Trains may not be in a route
1804  {
1805  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1806  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1807  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1808  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1809  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1810  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1811  FirstPair.second).GetELink() == TempELink))
1812  {
1813  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1814  }
1815  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1816  SecondPair.second).GetELink() == TempELink))
1817  {
1818  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1819  }
1820  AllRoutes->CheckMapAndRoutes(8); // test
1821  }
1822  if(LagElement > -1)
1823  // not entering at a continuation so can deal with train leaving the lag element
1824  {
1826  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1827  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1828  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1829 
1830  TPrefDirElement PrefDirElement;
1831  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1832  // as this is a 16x16 graphic
1834  {
1836  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1837  }
1838  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1839  {
1840  int RouteNumber;
1841  TrainGone = true;
1842  // flag to indicate train to be deleted - outside this function
1844  {
1845  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1846  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1847  // calc distance from & inc last signal to exit
1848  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1849  int NewLastElement = 0, NewLastExitPos = 0;
1850  // need above because can't change LastElement & LastExitPos until both new values obtained
1851  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1852  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1853  LastElement).TrackType != Points))
1854  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1855  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1856  // leave CumDistance as it was in these circumstances.
1857  {
1858  if(LastExitPos < 2)
1859  {
1860  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1861  }
1862  else
1863  {
1864  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1865  }
1866  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1867  if(NewLastElement == -1)
1868  // this will catch buffers or any other connection failure
1869  {
1870  break; //throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain"); //dropped at v2.15.0 because of Brent Mackie's error file of
1871  } //10/02/23, had two continuations linked with no signal between
1872  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)]; //so when train exited this routine tracked
1873  if(NewLastExitPos == -1) //back to the entry continuation which had no further connection - doesn't need to be an error at all!
1874  {
1875  break; //throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain"); //dropped at v2.15.0 because of Brent Mackie's error file of 10/02/23 , see above
1876  }
1877  LastElement = NewLastElement;
1878  LastExitPos = NewLastExitPos;
1879  }
1880  // if at signal add this in too (may not be signal if 'break;' encountered but doesn't matter)
1881  if(CumDistance < 1200)
1882  {
1883  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1884  }
1885  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1886  // else use 1200m - CumDistance
1887  int FirstDistance = 0;
1888  if(CumDistance >= 1200)
1889  {
1890  FirstDistance = 100;
1891  }
1892  else
1893  {
1894  FirstDistance = 1200 - CumDistance;
1895  }
1896  if(FirstDistance < 100)
1897  {
1898  FirstDistance = 100; // don't allow < 100
1899  }
1900  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1901  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1902  if(ExitSpeedFull > 20.0)
1903  {
1904  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1905  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1906  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1907  // 4320.0 = 3.6 * 1200, .0 to make it a double
1908  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1909  }
1910  else
1911  {
1912  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs between each action
1913  ContinuationAutoSigEntry.SecondDelay = 120.0;
1914  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1915  }
1916  ContinuationAutoSigEntry.AccessNumber = 0;
1917  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1919  {
1921  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1922  VectorIT++)
1923  {
1924  if(VectorIT->RouteNumber == RouteNumber)
1925  {
1926  // another train has passed out of same route so erase earlier entry
1927  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1928  break;
1929  }
1930  }
1931  }
1932  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1933  }
1935  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1936  Display->Update();
1937  // need to keep this since Update() not called for PlotSmallOutput as too slow
1938  Utilities->CallLogPop(659);
1939  return;
1940  }
1941  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1942  if(LeadElement > -1)
1943  {
1944  TTrackElement &TE = Track->TrackElementAt(224, LeadElement); //added at v2.13.0 for brevity
1945  if(TE.Config[LeadExitPos] == Signal)
1946  // changed to lead so reset early
1947  {
1948  LastSigPassedFailed = false; //used to cancel route elements up to next signal for autosigs route
1949  TE.Attribute = 0; // red
1950  int RouteNumber; //only used for autosigs routes
1951  //add chance to fail when train passes a signal
1952  if((random(Utilities->SignalChangeEventsPerFailure) == 0) && !TE.Failed && (Utilities->FailureMode != FNil) &&
1953  (TrainMode == Timetable) && !TE.CallingOnSet) //can't fail twice, calling on signal can't fail
1954  {
1956  IFE.TVPos = LeadElement;
1957  TE.Failed = true;
1958  Display->WarningLog(19, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Signal failed at " + TE.ElementID);
1959  PerfLogForm->PerformanceLog(42, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: Signal failed at " + TE.ElementID);
1960  TrainController->StopTTClockMessage(129, "Signal at " + TE.ElementID +
1961  " failed when changing aspect.\nTrains can only pass under signaller control.");
1962  AllRoutes->RebuildRailwayFlag = true; //force ClearandRebuildRailway at next clock tick
1963  LastSigPassedFailed = true;
1964  //set repair time, random value in minutes between 10 and 179
1965  double FailureMinutes = double(random(Utilities->MaxRandomRepairTime) + Utilities->FixedMinRepairTime); //between 10 and 179 minutes at random
1966  TDateTime RepairTime = TrainController->TTClockTime + TDateTime(FailureMinutes / 1440);
1967  IFE.RepairTime = RepairTime;
1969  Track->FailedSignalsVector.push_back(IFE); //rearwards signals will be set when LagElement leaves signal
1970  }
1971  TE.CallingOnSet = false;
1972  // don't plot if zoomed out
1973  if(!Display->ZoomOutFlag)
1974  {
1976  }
1977  // covers signal resetting in same direction
1978  }
1979  }
1981  {
1982  AllRoutes->RebuildRailwayFlag = true; //added at v2.13.0 to replot signal after train left in case it had failed
1983  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1984  {
1985  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1986  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1987  TPrefDirElement PrefDirElement;
1988  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1990  {
1992  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1993  }
1995  LockedVectorNumber)))
1996  {
1998  }
1999  }
2000  }
2001  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
2002  {
2003  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
2005  // don't plot if zoomed out
2006  if(!Display->ZoomOutFlag)
2007  {
2009  }
2010  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
2011  }
2013  {
2014  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
2015  {
2016  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
2017  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
2018  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
2019  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
2020  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
2021  int RouteNumber;
2023  // already know it's an autosigsroute, this is just to get the RouteNumber
2024  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
2025  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
2026  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
2027  int RouteNumber2;
2029  // already know it's an autosigsroute, this is just to get the RouteNumber
2030  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
2031  // note that if not in a route (as likely) then RouteNumber2 set to -1
2032  {
2033  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
2034  // this was in the 1.3.0 addition but without the condition
2035  }
2036  // end of 1.3.2 addition
2037  // end of 1.3.0.addition
2038  }
2039  TPrefDirElement PrefDirElement;
2040  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
2042  {
2044  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
2045  }
2046  }
2047  }
2048  }
2049  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
2050  if(Straddle == LeadMid)
2051  {
2052  AllowedToPassRedSignal = false;
2053  // if had been allowed to pass then at this point it will move half onto signal so can be reset
2054  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
2055  if(DerailPending)
2056  // set during last GetLeadElement, but only act on it when train fully on offending point
2057  // i.e. next time Straddle reaches LeadMid
2058  {
2059  Derailed = true;
2060  DerailPending = false;
2064  Utilities->CallLogPop(657);
2065  return;
2066  }
2073  Straddle = MidLag;
2074  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
2075  // LeadElement during this function (note that if stopped at signal then won't get this far)
2076  if(LeadElement > -1)
2077  {
2079  // i.e an exit continuation only
2080  // if don't exclude entry continuations then can't progress past it
2081  {
2082  LeadElement = -1;
2083  }
2084  else
2085  {
2086  GetLeadElement(0);
2087  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
2089  if(Stopped())
2090  {
2091  if(TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
2092  {
2093  TrainHasFailed(6);
2094  }
2095  Utilities->CallLogPop(658);
2096  return; // i.e. don't move forward one step if next element is a red signal
2097  }
2098  }
2099  }
2100  }
2101  if(LagElement > -1)
2102  {
2103  // below are the actions required at both half moves for LagElement > -1
2105 
2106  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
2107  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
2108  // need to do this for each half element
2109 
2110  TPrefDirElement PrefDirElement;
2111  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
2112  {
2113  int RouteNumber; // holder for call below - not used
2115  {
2116  if(Utilities->clTransparent == TColor(0xFFFFFF))
2117  // change to black for a white background
2118  {
2120  // only applies for AutoSigs Route in case was locked & timed out
2121  }
2122  else
2123  // change to white for a dark background
2124  {
2126  // only applies for AutoSigs Route in case was locked & timed out
2127  }
2129  }
2130  }
2132  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
2133  // or a train on the opposite track - needs to be replotted
2134  }
2135  // update all array values
2136  HOffset[3] = HOffset[2];
2137  HOffset[2] = HOffset[1];
2138  HOffset[1] = HOffset[0];
2139  VOffset[3] = VOffset[2];
2140  VOffset[2] = VOffset[1];
2141  VOffset[1] = VOffset[0];
2142  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
2143 
2144  BackgroundPtr[3] = BackgroundPtr[2];
2145  BackgroundPtr[2] = BackgroundPtr[1];
2146  BackgroundPtr[1] = BackgroundPtr[0];
2147  BackgroundPtr[0] = TempPtr;
2148 
2149  // update headcode graphics depending on Lead entry value
2150  if(LeadElement > -1) // if Lead is -1 then stays as is
2151  {
2153  {
2154  for(int x = 0; x < 4; x++)
2155  {
2156  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
2157  }
2158  }
2159  else
2160  {
2161  for(int x = 0; x < 4; x++)
2162  {
2164  }
2165  }
2166  }
2167  if(TrainMode == Timetable)
2168  {
2170  }
2171  else
2172  {
2174  }
2176 
2177  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
2178  if(LeadElement > -1)
2179  {
2180  if(Straddle == MidLag)
2181  // just about to move half onto the new lead element
2182  {
2184  // pick up new background bitmap [0]
2186  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
2187  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
2188  // check if own ID for entry at continuation, else crashes into itself!
2189  {
2190  // OK if crossing on a bridge
2191  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
2192  if(OtherTrainEntryPos == -1)
2193  {
2194  throw Exception("Error - OtherTrainEntryPos not set");
2195  }
2196  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
2197  // LeadEntryPos for rear end crashes
2198  (LeadExitPos == OtherTrainEntryPos))
2199  // LeadExitPos for head-on crashes
2200  {
2202  Crashed = true; // only set if Straddle = MidLag
2203  CallingOnFlag = false;
2204  // in case was set, need to disable call on if call on button had been pressed
2205  }
2206  }
2207  else if(MidElement > -1) // will be -1 for continuation entries
2208  {
2209  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
2210  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
2211  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
2212  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
2213  int OtherTrainID = -1;
2214  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
2215  {
2216  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
2217  {
2218  TrainCrashedInto = OtherTrainID;
2219  Crashed = true; // only set if Straddle = MidLag
2220  CallingOnFlag = false;
2221  // in case was set, need to disable call on if call on button had been pressed
2222  }
2223  }
2224  }
2225  }
2226  else
2227  {
2229  // pick up new background bitmap [0]
2231  }
2232  PlotElement[0] = LeadElement;
2234  PlotTrainGraphic(12, 0, Display);
2235  }
2236  if(MidElement > -1)
2237  {
2238  PlotElement[2] = MidElement;
2240  PlotTrainGraphic(1, 2, Display);
2241  }
2242  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
2243  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
2244  if(Straddle == MidLag)
2245  {
2246  if(MidElement > -1)
2247  {
2248  PlotElement[1] = MidElement;
2250  PlotTrainGraphic(2, 1, Display);
2251  }
2252  if(LagElement > -1)
2253  {
2254  PlotElement[3] = LagElement;
2256  PlotTrainGraphic(3, 3, Display);
2257  }
2258  }
2259  else // Straddle == LeadMidLag
2260  {
2261  if(LeadElement > -1)
2262  {
2263  PlotElement[1] = LeadElement;
2265  PlotTrainGraphic(4, 1, Display);
2266  }
2267  if(MidElement > -1)
2268  {
2269  PlotElement[3] = MidElement;
2271  PlotTrainGraphic(5, 3, Display);
2272  }
2273  }
2274  if(Crashed)
2275  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
2276  {
2281  // in case was set, need to disable call on if call on button had been pressed
2288  Straddle = LeadMidLag;
2289  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
2290  Display->Update();
2291  // resurrected when Update() dropped from PlotOutput etc
2292  Utilities->CallLogPop(660);
2293  return;
2294  }
2295  // deal here with station stops & pass times after all replotting done but before Straddle changed
2296  if(TrainMode == Timetable)
2297  {
2298  if(Straddle == LeadMidLag)
2299  {
2300  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2301  {
2302  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2303  // to point to the location arrival entry - before a change of direction
2304  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2305  bool StopRequired = false;
2306  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired); //excludes continuations
2307  if(TTVPos > -1) // -1 if can't find it or if name is ""
2308  {
2309  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2310  // or a station where next element contains a train or a stop signal, if so
2311  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2312  // to test the actual track the train is on since it can't be a platform
2313  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2314  TTrackElement NextTrackElement; // default for now
2315  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2316  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2317  bool TrainAtStopLinkPos3 = (LeadTrackElement.StationEntryStopLinkPos3 == LeadEntryPos);
2318  bool TrainAtStopLinkPos4 = (LeadTrackElement.StationEntryStopLinkPos4 == LeadEntryPos);
2319  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2320  int NextElementEntryPos = -1;
2321  int NextElementExitPos = -1;
2322  bool TrainOnNextElement = false;
2323  bool StopSignalAtNextElement = false;
2324  if(ForwardConnection)
2325  // if no forward connection can't derive anything from it without errors
2326  {
2327  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2328  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2329  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2330  // this is only for signals so no need to worry about points ambiguity
2331  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2332  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2333  }
2334  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || TrainAtStopLinkPos3 || TrainAtStopLinkPos4 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2335  {
2336  if(TTVPos > 0)
2337  {
2339  ActionVectorEntryPtr += TTVPos;
2340  }
2341  if(StopRequired)
2342  {
2343  StoppedAtLocation = true;
2344  StoppedAtSignal = false;
2345  // may have been set earlier at line 925 so need to reset as
2346  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2347  // in zoom out mode
2348  if(!TrainFailed)
2349  {
2351  // pale green
2352  }
2354  ActualArrivalTime = TrainController->TTClockTime; //added at v2.13.0
2356  {
2357  TimeTimeLocArrived = true;
2358  // used in case of later signaller control, when need to know
2359  // whether had arrived or not, to avoid sending the arrival
2360  // message twice, see TInterface::TimetableControl1Click
2361  }
2362  }
2363  else
2364  {
2366  }
2368  {
2370  }
2371  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2373  }
2374  }
2375  }
2376  }
2377  }
2378  if(Straddle == MidLag)
2379  {
2380  Straddle = LeadMidLag;
2381  FirstHalfMove = false;
2382  }
2383  else if(Straddle == LeadMidLag)
2384  {
2385  Straddle = LeadMid;
2386  FirstHalfMove = true;
2387  }
2388  else if(Straddle == LeadMid)
2389  {
2390  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2391  }
2392  if(TrainFailurePending) // ok, moving but PlotElements set above
2393  {
2394  TrainHasFailed(7);
2395  }
2396  Display->Update();
2397  // need to keep this since Update() not called for PlotSmallOutput as too slow
2398  Utilities->CallLogPop(661);
2399 }
2400 
2401 // ----------------------------------------------------------------------------
2402 
2403 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2404 {
2405  switch(CodeChar)
2406  {
2407  case '0':
2408  return(RailGraphics->Code0);
2409 
2410  case '1':
2411  return(RailGraphics->Code1);
2412 
2413  case '2':
2414  return(RailGraphics->Code2);
2415 
2416  case '3':
2417  return(RailGraphics->Code3);
2418 
2419  case '4':
2420  return(RailGraphics->Code4);
2421 
2422  case '5':
2423  return(RailGraphics->Code5);
2424 
2425  case '6':
2426  return(RailGraphics->Code6);
2427 
2428  case '7':
2429  return(RailGraphics->Code7);
2430 
2431  case '8':
2432  return(RailGraphics->Code8);
2433 
2434  case '9':
2435  return(RailGraphics->Code9);
2436 
2437  case 'A':
2438  return(RailGraphics->CodeA);
2439 
2440  case 'B':
2441  return(RailGraphics->CodeB);
2442 
2443  case 'C':
2444  return(RailGraphics->CodeC);
2445 
2446  case 'D':
2447  return(RailGraphics->CodeD);
2448 
2449  case 'E':
2450  return(RailGraphics->CodeE);
2451 
2452  case 'F':
2453  return(RailGraphics->CodeF);
2454 
2455  case 'G':
2456  return(RailGraphics->CodeG);
2457 
2458  case 'H':
2459  return(RailGraphics->CodeH);
2460 
2461  case 'I':
2462  return(RailGraphics->CodeI);
2463 
2464  case 'J':
2465  return(RailGraphics->CodeJ);
2466 
2467  case 'K':
2468  return(RailGraphics->CodeK);
2469 
2470  case 'L':
2471  return(RailGraphics->CodeL);
2472 
2473  case 'M':
2474  return(RailGraphics->CodeM);
2475 
2476  case 'N':
2477  return(RailGraphics->CodeN);
2478 
2479  case 'O':
2480  return(RailGraphics->CodeO);
2481 
2482  case 'P':
2483  return(RailGraphics->CodeP);
2484 
2485  case 'Q':
2486  return(RailGraphics->CodeQ);
2487 
2488  case 'R':
2489  return(RailGraphics->CodeR);
2490 
2491  case 'S':
2492  return(RailGraphics->CodeS);
2493 
2494  case 'T':
2495  return(RailGraphics->CodeT);
2496 
2497  case 'U':
2498  return(RailGraphics->CodeU);
2499 
2500  case 'V':
2501  return(RailGraphics->CodeV);
2502 
2503  case 'W':
2504  return(RailGraphics->CodeW);
2505 
2506  case 'X':
2507  return(RailGraphics->CodeX);
2508 
2509  case 'Y':
2510  return(RailGraphics->CodeY);
2511 
2512  case 'Z':
2513  return(RailGraphics->CodeZ);
2514 
2515  case 'a':
2516  return(RailGraphics->Code_a);
2517 
2518  case 'b':
2519  return(RailGraphics->Code_b);
2520 
2521  case 'c':
2522  return(RailGraphics->Code_c);
2523 
2524  case 'd':
2525  return(RailGraphics->Code_d);
2526 
2527  case 'e':
2528  return(RailGraphics->Code_e);
2529 
2530  case 'f':
2531  return(RailGraphics->Code_f);
2532 
2533  case 'g':
2534  return(RailGraphics->Code_g);
2535 
2536  case 'h':
2537  return(RailGraphics->Code_h);
2538 
2539  case 'i':
2540  return(RailGraphics->Code_i);
2541 
2542  case 'j':
2543  return(RailGraphics->Code_j);
2544 
2545  case 'k':
2546  return(RailGraphics->Code_k);
2547 
2548  case 'l':
2549  return(RailGraphics->Code_l);
2550 
2551  case 'm':
2552  return(RailGraphics->Code_m);
2553 
2554  case 'n':
2555  return(RailGraphics->Code_n);
2556 
2557  case 'o':
2558  return(RailGraphics->Code_o);
2559 
2560  case 'p':
2561  return(RailGraphics->Code_p);
2562 
2563  case 'q':
2564  return(RailGraphics->Code_q);
2565 
2566  case 'r':
2567  return(RailGraphics->Code_r);
2568 
2569  case 's':
2570  return(RailGraphics->Code_s);
2571 
2572  case 't':
2573  return(RailGraphics->Code_t);
2574 
2575  case 'u':
2576  return(RailGraphics->Code_u);
2577 
2578  case 'v':
2579  return(RailGraphics->Code_v);
2580 
2581  case 'w':
2582  return(RailGraphics->Code_w);
2583 
2584  case 'x':
2585  return(RailGraphics->Code_x);
2586 
2587  case 'y':
2588  return(RailGraphics->Code_y);
2589 
2590  case 'z':
2591  return(RailGraphics->Code_z);
2592 
2593  default:
2594  return(RailGraphics->TempHeadCode);
2595  }
2596 }
2597 
2598 // ----------------------------------------------------------------------------
2599 
2600 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2601 {
2602  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2603  if(Code.Length() != 4)
2604  {
2605  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2606  }
2607  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2608  {
2609  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2610  }
2611  if(BackgroundColour != clB5G5R5)
2612  // i.e. not the basic graphic colour as loaded from resource file
2613  {
2614  for(int x = 0; x < 4; x++)
2615  {
2617  }
2618  }
2619  Utilities->CallLogPop(1484);
2620 }
2621 
2622 // ----------------------------------------------------------------------------
2623 
2624 void TTrain::GetLeadElement(int Caller)
2625 // assumes Mid & Lag already set, sets LeadElement,
2626 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2627 {
2628  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2629  DerailPending = false;
2633  {
2634  // attr 0=straight, - links 0 & 1 (0 = lead)
2635  // attr 1=diverging, - links 2 & 3 (2 = lead)
2636  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2637  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2638 
2639  // if enter at lead, exit at whatever attr set at
2640  // if enter at lag, exit at lead, but set derail wrt attribute
2641  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2642  {
2643  LeadExitPos = 1;
2644  }
2645 
2646  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2647  // best to be on safe side
2648  else if(LeadEntryPos == 0)
2649  {
2650  LeadEntryPos = 2;
2651  LeadExitPos = 3;
2652  }
2653  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2654  {
2655  LeadEntryPos = 0;
2656  LeadExitPos = 1;
2657  }
2658  else if(LeadEntryPos == 2)
2659  {
2660  LeadExitPos = 3;
2661  }
2662  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2663  {
2664  LeadExitPos = 0;
2665  }
2666  else if(LeadEntryPos == 1)
2667  {
2668  LeadExitPos = 0;
2669  DerailPending = true;
2670  }
2671  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2672  {
2673  LeadExitPos = 0;
2674  DerailPending = true;
2675  }
2676  else if(LeadEntryPos == 3)
2677  {
2678  LeadExitPos = 0;
2679  }
2680  }
2681  else if(LeadEntryPos == 0)
2682  {
2683  LeadExitPos = 1;
2684  }
2685  else if(LeadEntryPos == 1)
2686  {
2687  LeadExitPos = 0;
2688  }
2689  else if(LeadEntryPos == 2)
2690  {
2691  LeadExitPos = 3;
2692  }
2693  else if(LeadEntryPos == 3)
2694  {
2695  LeadExitPos = 2;
2696  }
2697  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2698 /* signal check moved to Update() function
2699  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2700  && (TrackElement.Attribute == 0))//0 = red
2701  {
2702  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2703  }
2704  else
2705  {
2706  StoppedAtSignal = false;
2707  }
2708 */
2709  Utilities->CallLogPop(662);
2710 }
2711 
2712 // ----------------------------------------------------------------------------
2713 
2714 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2715 {
2716  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2717  switch(Link)
2718  {
2719  case 1:
2720  {
2721  HOffset = 0;
2722  VOffset = 0;
2723  break;
2724  }
2725 
2726  case 2:
2727  {
2728  HOffset = 4;
2729  VOffset = 0;
2730  break;
2731  }
2732 
2733  case 3:
2734  {
2735  HOffset = 8;
2736  VOffset = 0;
2737  break;
2738  }
2739 
2740  case 4:
2741  {
2742  HOffset = 0;
2743  VOffset = 4;
2744  break;
2745  }
2746 
2747  case 6:
2748  {
2749  HOffset = 8;
2750  VOffset = 4;
2751  break;
2752  }
2753 
2754  case 7:
2755  {
2756  HOffset = 0;
2757  VOffset = 8;
2758  break;
2759  }
2760 
2761  case 8:
2762  {
2763  HOffset = 4;
2764  VOffset = 8;
2765  break;
2766  }
2767 
2768  case 9:
2769  {
2770  HOffset = 8;
2771  VOffset = 8;
2772  break;
2773  }
2774 
2775  default:
2776  {
2777  throw Exception("Error in GetOffsetValues - Link value wrong");
2778  }
2779  }
2780  Utilities->CallLogPop(674);
2781 }
2782 
2783 // ---------------------------------------------------------------------------
2784 
2785 bool TTrain::LowEntryValue(int EntryLink) const
2786 {
2787 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2788  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2789 */
2790  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2791  {
2792  return(true);
2793  }
2794  else
2795  {
2796  return(false);
2797  }
2798 }
2799 
2800 // ---------------------------------------------------------------------------
2801 
2802 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2803 {
2804  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2805  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2806  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2807  // default values
2808  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2809 
2810  TAllRoutes::TRouteType RouteType;
2811 
2812  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2813 
2814  TRect SourceRect, DestRect;
2815 
2816  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2817  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2818  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2819  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2820 
2821  TempGraphic->PixelFormat = pf8bit;
2822  TempGraphic->Width = 16;
2823  TempGraphic->Height = 16;
2824  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2825 
2826  if(TempElement.TrackType == Points)
2827  {
2828  TempGraphic->Assign(TempElement.GraphicPtr);
2829  TempGraphic->Transparent = true;
2830  TempGraphic->TransparentColor = Utilities->clTransparent;
2831  if(RouteType == TAllRoutes::AutoSigsRoute)
2832  {
2833  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2834  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2835  }
2836  else
2837  {
2838  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2839  }
2840  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2841  }
2842  else if(TempElement.TrackType == GapJump) // plot set gap
2843  {
2844  if(TempElement.SpeedTag == 88)
2845  {
2846  TempGraphic->Assign(RailGraphics->gl88set);
2847  }
2848  else if(TempElement.SpeedTag == 89)
2849  {
2850  TempGraphic->Assign(RailGraphics->gl89set);
2851  }
2852  else if(TempElement.SpeedTag == 90)
2853  {
2854  TempGraphic->Assign(RailGraphics->gl90set);
2855  }
2856  else if(TempElement.SpeedTag == 91)
2857  {
2858  TempGraphic->Assign(RailGraphics->gl91set);
2859  }
2860  else if(TempElement.SpeedTag == 92)
2861  {
2862  TempGraphic->Assign(RailGraphics->gl92set);
2863  }
2864  else if(TempElement.SpeedTag == 93)
2865  {
2866  TempGraphic->Assign(RailGraphics->bm93set);
2867  }
2868  else if(TempElement.SpeedTag == 94)
2869  {
2870  TempGraphic->Assign(RailGraphics->bm94set);
2871  }
2872  else if(TempElement.SpeedTag == 95)
2873  {
2874  TempGraphic->Assign(RailGraphics->gl95set);
2875  }
2876  TempGraphic->Transparent = true;
2877  TempGraphic->TransparentColor = Utilities->clTransparent;
2878  if(RouteType == TAllRoutes::AutoSigsRoute)
2879  {
2880  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2881  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2882  }
2883  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2884  }
2885  // new for version 0.6
2886  else if(TempElement.TrackType == SignalPost)
2887  {
2888  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2889  {
2890  for(int x = 0; x < 40; x++)
2891  {
2892  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2893  // need to stop aspect
2894  {
2895  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2896  break;
2897  }
2898  }
2899  }
2900  else // normal signal
2901  {
2902  TempGraphic->Assign(TempElement.GraphicPtr);
2903  // GraphicPtr set to normal signal in a signal track element
2904  }
2905  TempGraphic->Transparent = true;
2906  TempGraphic->TransparentColor = Utilities->clTransparent;
2907  if(RouteType == TAllRoutes::AutoSigsRoute)
2908  {
2909  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2910  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2911  }
2912  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2913  }
2914  else
2915  {
2916  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2917  // can't name points gaps or signals so 'else' OK
2918  bool FoundFlag;
2919  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2920  if(FoundFlag)
2921  {
2923  {
2924  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2925  TempGraphic->Assign(RailGraphics->bmName);
2926  TempGraphic->Transparent = true;
2927  TempGraphic->TransparentColor = Utilities->clTransparent;
2928  if(RouteType == TAllRoutes::AutoSigsRoute)
2929  {
2930  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2931  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2932  }
2933  else
2934  {
2935  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2936  }
2937  // draw track on top
2938  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2939  }
2940  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2941  {
2942  TempGraphic->Assign(TempElement.GraphicPtr);
2943  TempGraphic->Transparent = true;
2944  TempGraphic->TransparentColor = Utilities->clTransparent;
2945  // note that can't be an AutoSigsRoute
2946  // now overlay the LC central portion
2947  int BDVectorPos = -1; //not used
2948  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2949  {
2950  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2951  }
2952  else
2953  {
2954  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2955  }
2956  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2957  }
2958  else
2959  {
2960  TempGraphic->Assign(TempElement.GraphicPtr);
2961  TempGraphic->Transparent = true;
2962  TempGraphic->TransparentColor = Utilities->clTransparent;
2963  if(RouteType == TAllRoutes::AutoSigsRoute)
2964  {
2965  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2966  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2967  }
2968  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2969  }
2970  }
2971  else
2972  {
2973  TempGraphic->Assign(TempElement.GraphicPtr);
2974  TempGraphic->Transparent = true;
2975  TempGraphic->TransparentColor = Utilities->clTransparent;
2976  if(RouteType == TAllRoutes::AutoSigsRoute)
2977  {
2978  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2979  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2980  }
2981  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2982  }
2983  }
2984  delete TempGraphic;
2985  Utilities->CallLogPop(675);
2986 }
2987 
2988 // ---------------------------------------------------------------------------
2989 
2990 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2991 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2992 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2993 /*
2994 
2995  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2996  {
2997  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2998  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2999  TAllRoutes::TRouteType RouteType;
3000  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
3001  // default values
3002  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
3003  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
3004  TRect SourceRect, DestRect, ScreenSourceRect;
3005  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
3006 
3007  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
3008  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3009  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3010 
3011  //add text & user graphics if any to *GraphicPtr prior to adding the track
3012  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
3013  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
3014  int Right = Left + 8;
3015  int Bottom = Top + 8;
3016  ScreenSourceRect.init(Left, Top, Right, Bottom);
3017  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
3018  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
3019 
3020  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
3021  TempGraphic->PixelFormat = pf8bit;
3022  TempGraphic->Width = 16;
3023  TempGraphic->Height = 16;
3024 
3025  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
3026  SourceGraphic->PixelFormat = pf8bit;
3027  SourceGraphic->Width = 16;
3028  SourceGraphic->Height = 16;
3029  SourceGraphic->Transparent = true;
3030  SourceGraphic->TransparentColor = Utilities->clTransparent;
3031 
3032  if (TempElement.TrackType == Points)
3033  {
3034  TempGraphic->Assign(TempElement.GraphicPtr);
3035  TempGraphic->Transparent = true;
3036  TempGraphic->TransparentColor = Utilities->clTransparent;
3037  if (RouteType == TAllRoutes::AutoSigsRoute)
3038  {
3039  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3040  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3041  }
3042  else
3043  {
3044  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
3045  }
3046  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3047  }
3048  else if (TempElement.TrackType == GapJump) // plot set gap
3049  {
3050  if (TempElement.SpeedTag == 88)
3051  TempGraphic->Assign(RailGraphics->gl88set);
3052  else if (TempElement.SpeedTag == 89)
3053  TempGraphic->Assign(RailGraphics->gl89set);
3054  else if (TempElement.SpeedTag == 90)
3055  TempGraphic->Assign(RailGraphics->gl90set);
3056  else if (TempElement.SpeedTag == 91)
3057  TempGraphic->Assign(RailGraphics->gl91set);
3058  else if (TempElement.SpeedTag == 92)
3059  TempGraphic->Assign(RailGraphics->gl92set);
3060  else if (TempElement.SpeedTag == 93)
3061  TempGraphic->Assign(RailGraphics->bm93set);
3062  else if (TempElement.SpeedTag == 94)
3063  TempGraphic->Assign(RailGraphics->bm94set);
3064  else if (TempElement.SpeedTag == 95)
3065  TempGraphic->Assign(RailGraphics->gl95set);
3066  TempGraphic->Transparent = true;
3067  TempGraphic->TransparentColor = Utilities->clTransparent;
3068  if (RouteType == TAllRoutes::AutoSigsRoute) {
3069  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3070  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3071  }
3072  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3073  }
3074  // new for version 0.6
3075  else if (TempElement.TrackType == SignalPost)
3076  {
3077  if (TempElement.SigAspect == TTrackElement::GroundSignal)
3078  {
3079  for (int x = 0; x < 40; x++)
3080  {
3081  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
3082  // need to stop aspect
3083  {
3084  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
3085  break;
3086  }
3087  }
3088  }
3089  else // normal signal
3090  {
3091  TempGraphic->Assign(TempElement.GraphicPtr);
3092  // GraphicPtr set to normal signal in a signal track element
3093  }
3094  TempGraphic->Transparent = true;
3095  TempGraphic->TransparentColor = Utilities->clTransparent;
3096  if (RouteType == TAllRoutes::AutoSigsRoute) {
3097  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3098  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3099  }
3100  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3101  }
3102  else {
3103  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
3104  // can't name points gaps or signals so 'else' OK
3105  bool FoundFlag;
3106  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
3107  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
3108  if (FoundFlag)
3109  {
3110  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
3111  {
3112  GraphicPtr->Canvas->CopyRect(DestRect,
3113  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
3114  TempGraphic->Assign(RailGraphics->bmName);
3115  TempGraphic->Transparent = true;
3116  TempGraphic->TransparentColor = Utilities->clTransparent;
3117  if (RouteType == TAllRoutes::AutoSigsRoute)
3118  {
3119  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3120  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3121  }
3122  else
3123  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
3124  // draw track on top
3125  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3126  SourceRect);
3127  }
3128  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
3129  TempGraphic->Assign(TempElement.GraphicPtr);
3130  TempGraphic->Transparent = true;
3131  TempGraphic->TransparentColor = Utilities->clTransparent;
3132  // note that can't be an AutoSigsRoute
3133  // now overlay the LC central portion
3134  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
3135  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3136  SourceRect);
3137  }
3138  else {
3139  TempGraphic->Assign(TempElement.GraphicPtr);
3140  TempGraphic->Transparent = true;
3141  TempGraphic->TransparentColor = Utilities->clTransparent;
3142  if (RouteType == TAllRoutes::AutoSigsRoute) {
3143  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3144  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3145  }
3146  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3147  SourceRect);
3148  }
3149  }
3150  else {
3151  TempGraphic->Assign(TempElement.GraphicPtr);
3152  TempGraphic->Transparent = true;
3153  TempGraphic->TransparentColor = Utilities->clTransparent;
3154  if (RouteType == TAllRoutes::AutoSigsRoute) {
3155  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3156  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3157  }
3158  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3159  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
3160  }
3161  }
3162  delete TempGraphic;
3163  delete SourceGraphic;
3164  Utilities->CallLogPop();
3165  }
3166 */
3167 // ---------------------------------------------------------------------------
3168 
3169 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
3170 {
3171  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
3172  if(PlotElement[ArrayNumber] == -1)
3173  {
3174  Utilities->CallLogPop(676);
3175  return; // not plotted yet
3176  }
3177  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
3178  // set before plot so gap flashing stops first
3179  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3180  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
3181  // Only need to set ID for leading element, stays set until train finally leaves the element
3182  Plotted = true;
3183  Utilities->CallLogPop(677);
3184 }
3185 
3186 // ---------------------------------------------------------------------------
3187 
3188 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
3189 {
3190  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3191  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
3192 }
3193 
3194 // ---------------------------------------------------------------------------
3195 
3196 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
3197 {
3198  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
3199  HeadCode);
3200  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
3201  {
3202  Utilities->CallLogPop(678);
3203  return(true);
3204  }
3205  else
3206  {
3207  Utilities->CallLogPop(679);
3208  return(false);
3209  }
3210 }
3211 
3212 // ---------------------------------------------------------------------------
3213 
3214 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
3215 {
3216  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
3217  "," + HeadCode);
3218  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
3219  {
3220  Utilities->CallLogPop(680);
3221  return(true);
3222  }
3223  else
3224  {
3225  Utilities->CallLogPop(681);
3226  return(false);
3227  }
3228 }
3229 
3230 // ---------------------------------------------------------------------------
3231 
3232 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
3233 // test whether this train on a bridge on trackpos 0 & 1
3234 {
3235  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
3236  HeadCode);
3237  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
3238  {
3239  Utilities->CallLogPop(682);
3240  return(false);
3241  }
3242  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3244  {
3246  {
3247  throw Exception("Error, same train on two different bridge tracks");
3248  }
3249  else
3250  {
3251  Utilities->CallLogPop(684);
3252  return(true);
3253  }
3254  }
3255  else
3256  {
3257  Utilities->CallLogPop(685);
3258  return(false);
3259  }
3260 }
3261 
3262 // ---------------------------------------------------------------------------
3263 
3264 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
3265 // test whether this train on a bridge on trackpos 2 & 3
3266 {
3267  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
3268  HeadCode);
3269  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
3270  {
3271  Utilities->CallLogPop(686);
3272  return(false);
3273  }
3274  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3276  {
3277  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
3278  Utilities->CallLogPop(687);
3279  return(true);
3280  }
3281  else
3282  {
3283  Utilities->CallLogPop(688);
3284  return(false);
3285  }
3286 }
3287 
3288 // ---------------------------------------------------------------------------
3289 
3290 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3291 {
3292  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3293  AnsiString(EntryPos) + "," + HeadCode);
3294  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
3295 
3296  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
3297  if(Track->GapFlashFlag)
3298  {
3300  {
3303  Track->GapFlashFlag = false;
3304  }
3305  }
3306  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3307  {
3308  if(EntryPos == -1)
3309  {
3310  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3311  }
3312  if(EntryPos < 2)
3313  {
3315  }
3316  else
3317  {
3319  }
3320  }
3321  Utilities->CallLogPop(690);
3322 }
3323 
3324 // ---------------------------------------------------------------------------
3325 
3326 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3327 {
3328  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3329  AnsiString(EntryPos) + "," + HeadCode);
3330  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3331  {
3332  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3333  }
3334  else
3335  {
3336  if(EntryPos == -1)
3337  {
3338  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3339  }
3340  if(EntryPos < 2)
3341  {
3342  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 = -1;
3343  }
3344  else
3345  {
3346  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 = -1;
3347  }
3348  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
3349  // i.e. other train on track 2&3
3350  {
3351  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
3352  }
3353  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
3354  // i.e. other train on track 1&2
3355  {
3356  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
3357  }
3358  else
3359  {
3360  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3361  }
3362  }
3363  Utilities->CallLogPop(691);
3364 }
3365 
3366 // ---------------------------------------------------------------------------
3367 
3368 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3369 {
3370  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3371  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3372  int LockedVectorNumber;
3373 
3374  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3375  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3376  {
3377  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3378  Utilities->CallLogPop(692);
3379  return;
3380  }
3381  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3382  // i.e other track is in a marked route
3383  // LinkPos doesn't have to be the entry position for the above check
3384  {
3385  TRect SourceRect, DestRect;
3386  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3387  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3388  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3389  // identify the route element for the other track
3390  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3391  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3392  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3393  int FirstELink, SecondELink = -1;
3394  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3395  // must be at least one
3396  if(RoutePair2.first > -1)
3397  {
3398  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3399  }
3400  TPrefDirElement RouteElement;
3401  // Graphics::TBitmap *RouteGraphic;
3402  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3403  // i.e. other track is in RoutePair2
3404  {
3405  if(SecondELink == -1)
3406  {
3407  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3408  }
3409  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3410  // error if both have same Link number
3411  {
3412  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3413  }
3414  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3415  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3416  }
3417  else // other track is in RoutePair1
3418  {
3419  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3420  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3421  }
3422  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3423  DestGraphic->PixelFormat = pf8bit;
3424  DestGraphic->Width = 8;
3425  DestGraphic->Height = 8;
3426  DestGraphic->Transparent = true;
3427  // has to be transparent or will overwrite the track that the train has just left
3428  DestGraphic->TransparentColor = Utilities->clTransparent;
3429  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3430  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3431  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3432  // plot locked route marker for other route if appropriate
3433  TPrefDirElement PrefDirElement; // holder for next call, unused
3434  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3435  if(StraddleValue == LeadMidLag)
3436  {
3438  PrefDirElement, LockedVectorNumber))
3439  {
3440  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3441  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3442  }
3443  }
3444  delete DestGraphic;
3445  }
3446  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3447  // also can only be a bridge or trains either have already or soon will crash
3448  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3449  {
3450  Utilities->CallLogPop(695);
3451  return;
3452  }
3453  if(ElementEntryPos > 1) // other train is on track 01
3454  {
3456  {
3458  }
3459  }
3460  else // other train is on track 23
3461  {
3463  {
3465  }
3466  }
3467  Utilities->CallLogPop(696);
3468 }
3469 
3470 // ---------------------------------------------------------------------------
3471 
3472 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3473 {
3474  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3475  AnsiString(EntryPos) + "," + HeadCode);
3476  int RouteNumber;
3477  bool WrongRoute = false;
3478  TPrefDirElement RouteElement;
3480  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3481 
3482  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3483  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3484  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3485  {
3486  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3487  {
3488  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3489  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3490  {
3491  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3492  {
3493  // don't call for stub end routes
3495  }
3496  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3497  Utilities->CallLogPop(697);
3498  return;
3499  }
3500  }
3501  // also need to check for a route on a crossing diagonal
3502  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3503  int LinkNumber = TrackElement.Link[EntryPos];
3504  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3505  {
3506  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3507  {
3508  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3509  bool LogActionErrorCalled = false;
3510  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3511  if(LinkNumber == 1)
3512  {
3513  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3514  {
3515  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3516  {
3517  // don't call for stub end routes
3519  LogActionErrorCalled = true;
3520  }
3521  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3522  }
3523  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3524  // not else in case have different routes on each diagonal, though shouldn't be possible
3525  {
3526  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3527  {
3528  // don't call for stub end routes
3530  }
3531  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3532  }
3533  }
3534 
3535  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3536  else if(LinkNumber == 3)
3537  {
3538  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3539  {
3540  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3541  {
3542  // don't call for stub end routes
3544  LogActionErrorCalled = true;
3545  }
3546  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3547  }
3548  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3549  // not else in case have different routes on each diagonal, though shouldn't be possible
3550  {
3551  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3552  {
3553  // don't call for stub end routes
3555  }
3556  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3557  }
3558  }
3559 
3560  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3561  else if(LinkNumber == 7)
3562  {
3563  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3564  {
3565  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3566  {
3567  // don't call for stub end routes
3569  LogActionErrorCalled = true;
3570  }
3571  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3572  }
3573  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3574  // not else in case have different routes on each diagonal, though shouldn't be possible
3575  {
3576  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3577  {
3578  // don't call for stub end routes
3580  }
3581  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3582  }
3583  }
3584 
3585  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3586  else if(LinkNumber == 9)
3587  {
3588  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3589  {
3590  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3591  {
3592  // don't call for stub end routes
3594  LogActionErrorCalled = true;
3595  }
3596  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3597  }
3598  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3599  // not else in case have different routes on each diagonal, though shouldn't be possible
3600  {
3601  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3602  {
3603  // don't call for stub end routes
3605  }
3606  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3607  }
3608  }
3609  }
3610  }
3611  Utilities->CallLogPop(698);
3612  return; // no route on other track or no other track
3613  }
3614  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3615  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3616  {
3617  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3618  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new at v2.4.2 for points check - Xeon repoted it 30/05/20. He found that for routes that
3619  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3620  {
3621  if(RouteElement.GetELinkPos() == EntryPos)
3622  {
3623  Utilities->CallLogPop(699);
3624  return; // right direction
3625  }
3626  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3627  {
3628  Utilities->CallLogPop(700);
3629  return; // right direction (points)
3630  }
3631  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3632  {
3633  Utilities->CallLogPop(701);
3634  return; // right direction (points)
3635  }
3636  else if(RouteElement.GetXLinkPos() == EntryPos)
3637  {
3638  WrongRoute = true;
3639  break; // wrong direction
3640  }
3641  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3642  {
3643  WrongRoute = true;
3644  break; // wrong direction
3645  }
3646  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3647  {
3648  WrongRoute = true;
3649  break; // wrong direction
3650  }
3651  }
3652  }
3653  if(!WrongRoute)
3654  {
3655  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3656  }
3657  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3658  {
3659  // don't call for stub end routes
3661  }
3662  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3663  Utilities->CallLogPop(703);
3664 }
3665 
3666 // ---------------------------------------------------------------------------
3667 
3668 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3669 {
3670  if(BackgroundColour == NewBackgroundColour)
3671  {
3672  return; // don't replot if already correct
3673 
3674  }
3675  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3676  bool ColourError = false, ColourError2 = false;
3677 
3678  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3679  if(ColourError)
3680  {
3681  ColourError2 = true;
3682  }
3683  for(int x = 0; x < 4; x++)
3684  {
3685  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3686  if(ColourError)
3687  {
3688  ColourError2 = true;
3689  }
3690  }
3691  if(ColourError2)
3692  {
3694  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3695  }
3696  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3697  // of motion
3698  for(int x = 0; x < 4; x++)
3699  {
3700  PlotTrainGraphic(6, x, Disp);
3701  }
3702  BackgroundColour = NewBackgroundColour;
3703  Display->Update();
3704  // need to keep this since Update() not called for PlotSmallOutput as too slow
3705  Utilities->CallLogPop(704);
3706 }
3707 
3708 // ---------------------------------------------------------------------------
3709 
3710 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3711 /*
3712 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3713 
3714 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3715 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3716 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3717 full-element moves.
3718 
3719 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3720 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3721 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3722 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3723 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3724 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3725 In this case set the brake rate to maximum to stop as soon as possible.
3726 
3727 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3728 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3729 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3730 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3731 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3732 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3733 
3734 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3735 first to see whether buffers or continuation) in turn is examined: first the length of the
3736 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3737 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3738 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3739 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3740 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3741 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3742 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3743 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3744 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3745 siding then again emeregency braking may be necessary and a crash may result.
3746 
3747 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3748 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3749 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3750 buffer, then the train accelerates for half the element and brakes for the other half.
3751 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3752 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3753 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3754 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3755 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3756 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3757 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3758 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3759 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3760 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3761 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3762 
3763 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3764 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3765 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3766 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3767 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3768 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3769 MaxBrakeRate.
3770 
3771 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3772 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3773 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3774 
3775 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3776 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3777 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3778 
3779 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3780 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3781 when Straddle == LeadMidLag
3782 */
3783 {
3784  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3785  AnsiString(EntryPos) + "," + HeadCode);
3786  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3787  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3788  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3789  TrainInFrontInSignallerModeFlag = false;
3790  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3791  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3792  bool SignallerStopRequired = false;
3793 
3795  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3796 
3797  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3798 
3799  OneLengthAccelDecel = false;
3800  BrakeRate = 0;
3801  if(PowerAtRail < 1)
3802  {
3803  BrakeRate = CoastingBrakeRate; //brings train to a stop in 13km in 15min from starting speed of 100km/h (from research)
3804  }
3805 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3806  if(CurrentTrackVectorPosition > -1)
3807  {
3808  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3809  {
3810  if((EntryPos == 0) || (EntryPos == 2))
3811  {
3812  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3813  {
3814  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3815  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3816  }
3817  else
3818  {
3819  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3820  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3821  }
3822  }
3823  else if(EntryPos == 1)
3824  {
3825  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3826  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3827  }
3828  else // == 3
3829  {
3830  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3831  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3832  }
3833  }
3834  else
3835  {
3836  if(EntryPos > 1)
3837  {
3838  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3839  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3840  }
3841  else
3842  {
3843  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3844  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3845  }
3846  }
3847  EntryHalfLength = CurrentElementHalfLength;
3848  FrontElementLength = 2 * CurrentElementHalfLength;
3849  }
3850  else
3851  {
3852  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3853  }
3854  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3855  {
3856  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3857  }
3858  // check if zero entry speed with another train directly in front & if so remain stopped
3859  if((EntryPos > -1) && Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3860  {
3861  EntrySpeed = 0;
3862  ExitSpeedHalf = 0;
3863  ExitSpeedFull = 0;
3864  MaxExitSpeed = 0;
3865  BrakeRate = 0;
3866  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3867  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3868  StoppedForTrainInFront = true;
3869  TrainInFront = true;
3870  Utilities->CallLogPop(705);
3871  return;
3872  }
3873  // new at v2.4.0 - check for stopped and zero power
3874  if((EntrySpeed < 1) && PowerAtRail < 1)
3875  {
3876  EntrySpeed = 0;
3877  ExitSpeedHalf = 0;
3878  ExitSpeedFull = 0;
3879  MaxExitSpeed = 0;
3880  BrakeRate = 0;
3881  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3882  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3883  StoppedWithoutPower = true;
3884  Utilities->CallLogPop(2125);
3885  return;
3886  }
3887 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3888  if(BeingCalledOn)
3889  {
3890  LimitingSpeed = CallOnMaxSpeed;
3891  }
3892  else
3893  {
3894  LimitingSpeed = MaximumSpeedLimit;
3895  }
3896  if(LimitingSpeed > FrontElementSpeedLimit)
3897  {
3898  LimitingSpeed = FrontElementSpeedLimit;
3899  }
3900  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3901  {
3902  LimitingSpeed = MaxRunningSpeed;
3903  }
3904  FrontElementMaxSpeed = LimitingSpeed;
3905 
3906 /*
3907  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3908  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3909  (2) V/3.6 = U/3.6 - FT;
3910  (3) S = UT/3.6 - 0.5FT^2
3911  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3912 
3913  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3914  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3915  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3916  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3917  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3918 
3919  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3920  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3921  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3922 */
3923 
3924 // check if running past a red signal without permission
3925  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) &&
3926  (Track->TrackElementAt(353, CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal &&
3927  !Track->TrackElementAt(1553, CurrentTrackVectorPosition).CallingOnSet)
3928  { //CallingOnSet added at v2.14.0
3929  SPADFlag = true; // user has to intervene to reset & restart after spad
3930  }
3931  if(!SPADFlag)
3932  {
3933  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3934  // to begin with calc the maximum exit speed (assumes accelerating) and then reduce it if necessary
3935  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3936  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
3937  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
3938  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3939 
3940  double ExitSpeedAtMaxBraking;
3941  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3942  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3944  {
3945  ExitSpeedAtMaxBraking = 0;
3946  }
3947  else
3948  {
3949  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3950  }
3951  double SpeedToUse;
3952  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added at v2.4.2 because trains entering at a continuation with zero (or very low) speed
3953  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3954  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3955  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3956  {
3957  SpeedToUse = ExitSpeedAtMaxBraking;
3958  }
3959  else
3960  {
3961  SpeedToUse = LimitingSpeed;
3962  }
3963  if(ExitSpeedFull > SpeedToUse)
3964  {
3965  ExitSpeedFull = SpeedToUse;
3966  }
3967  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3968  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3969 
3970  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3971  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3972  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3973 
3974  do
3975  {
3976  RedSignalFlag = false;
3977  BuffersFlag = false;
3978  StationFlag = false;
3979  BuffersOrContinuationNowFlag = false;
3980  ContinuationNextFlag = false;
3981  // have to reset this after the above test
3982  // add current element length to CumulativeLength
3983  CumulativeLength += (2 * CurrentElementHalfLength);
3984  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3985  {
3986  SignallerStopRequired = true;
3987  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3988  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3989  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3990  if(SignallerStopBrakeRate < TempBR)
3991  {
3992  SignallerStopBrakeRate = TempBR;
3993  }
3994  }
3995  // first check for stops within the length of the current element, where don't want any more checks & don't want
3996  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3997  // during the last loop when the NextTrackVectorPosition was the signal.
3998 
3999  // check if current element is a buffer
4000  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
4001  {
4002  // no need to add in the length of this element to CumulativeLength as already included
4003  BuffersFlag = true;
4004  }
4005  // check if current element is a station stop
4006  if(TrainMode == Timetable)
4007  {
4008  bool StopRequired = false;
4009  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
4010  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
4011  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos) ||
4012  (Track->TrackElementAt(1642, CurrentTrackVectorPosition).StationEntryStopLinkPos3 == EntryPos) ||
4013  (Track->TrackElementAt(1643, CurrentTrackVectorPosition).StationEntryStopLinkPos4 == EntryPos)))
4014  {
4015  // no need to add in the length of element to CumulativeLength
4016  if(StopRequired)
4017  {
4018  StationFlag = true;
4019  }
4020  }
4021  }
4022  else
4023  {
4024  StationFlag = false;
4025  }
4026  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
4027  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
4028  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4029  {
4030  BuffersOrContinuationNowFlag = true;
4031  }
4032  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
4033  {
4034  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
4035  {
4036  if((EntryPos == 0) || (EntryPos == 2))
4037  {
4038  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
4039  {
4040  ExitPos = 1;
4041  }
4042  else
4043  {
4044  ExitPos = 3;
4045  }
4046  }
4047  else
4048  {
4049  ExitPos = 0;
4050  }
4051  }
4052  else
4053  {
4054  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4055  }
4056  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
4057  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4058  if(NextTrackVectorPosition > -1)
4059  {
4060  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
4061  // this test & section added at v0.6
4062  {
4063  if((NextEntryPos == 0) || (NextEntryPos == 2))
4064  {
4065  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
4066  {
4067  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
4068  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
4069  }
4070  else
4071  {
4072  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
4073  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
4074  }
4075  }
4076  else if(NextEntryPos == 1)
4077  {
4078  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
4079  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
4080  }
4081  else // == 3
4082  {
4083  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
4084  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
4085  }
4086  }
4087  else
4088  {
4089  if(NextEntryPos > 1)
4090  {
4091  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
4092  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
4093  }
4094  else
4095  {
4096  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
4097  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
4098  }
4099  }
4100  }
4101  else
4102  {
4103  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
4104  }
4105  // now check for stops, first cover those where don't want to add in length of next element
4106  // check if next element is a red signal - Attr 0,
4107  // note that this doesn't apply to trains stopped at a red signal since the signal position is
4108  // CurrentTrackVectorPosition not NextTrackVectorPosition
4109  bool StopRequired;
4110  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
4111  {
4112  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
4113  {
4114  // no need to add in the length of element to CumulativeLength
4115  RedSignalFlag = true;
4116  }
4117  // next element is a red signal
4118  }
4119  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
4120  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
4121  // at least one platform element free
4123  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition, NextEntryPos, TrainID))
4124  {
4125  // no need to add in the length of element to CumulativeLength
4126  if(StopRequired)
4127  {
4128  StationFlag = true;
4129  }
4130  }
4131  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
4132  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
4133  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
4134  {
4135  // no need to add in the length of element to CumulativeLength
4136  TrainInFrontInSignallerModeFlag = true;
4137  }
4138  // check if next element is a buffer, but if StepForwardFlag true then need to stop before reach the buffers
4139  else if((Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers) && !StepForwardFlag)
4140  {
4141  // need to add in the length of that element to CumulativeLength
4142  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
4143  BuffersFlag = true;
4144  }
4145  // check if next element is a station stop
4147  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
4148  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
4149  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos) || (Track->TrackElementAt(1644,
4150  NextTrackVectorPosition).StationEntryStopLinkPos3 == EntryPos) || (Track->TrackElementAt(1645,
4151  NextTrackVectorPosition).StationEntryStopLinkPos4 == EntryPos)))
4152  { // need to add in the length of that element to CumulativeLength if a stop required
4153  if(StopRequired)
4154  {
4155  StationFlag = true;
4156  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
4157  }
4158  }
4159  }
4160  //now can decide whether need to stop over CumulativeLength
4161  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
4162  {
4163  // have to come to a stop over CumulativeLength
4164  if(CumulativeLength == FrontElementLength)
4165  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
4166  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
4167  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
4168  // and if less than EntrySpeed then skip this section (don't need any acceleration)
4169  // if not calc speed at halfway point & if less than above set half speed to this value;
4170  // use constant acceleration in calculating half time point
4171  {
4172  MaxExitSpeed = 0;
4173  double MaxHalfSpeed;
4174  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
4175  // have to halve the element length, & can't be zero or negative so no need to test
4176  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4177  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
4178  {
4179  MaxHalfSpeed = FrontElementMaxSpeed;
4180  }
4181  else
4182  {
4183  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4184  }
4185  if(MaxHalfSpeed > (2 * EntrySpeed) && (PowerAtRail > 1)) //PowerAtRail condition added at v2.18.0
4186  // use 2x to prevent kangarooing at last element when had
4187  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
4188  {
4189  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4190  0.333334);
4191  bool HalfSpeedLimited = false;
4192  if(MaxHalfSpeed < ExitSpeedHalf)
4193  {
4194  ExitSpeedHalf = MaxHalfSpeed;
4195  HalfSpeedLimited = true;
4196  }
4197  if(PowerAtRail > 1)
4198  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4199  {
4200  // [km/h/3.6 = m/s]
4201  ExitTimeHalf =
4202  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4203  }
4204  else
4205  {
4206  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4207  }
4208  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
4209  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
4210  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
4211  // by a long braking period
4212  ExitSpeedFull = 0;
4213  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
4214  if(TempBrakeRate > MaxBrakeRate)
4215  {
4216  TempBrakeRate = MaxBrakeRate;
4217  }
4218  // shouldn't be but leave in anyway
4219  if(TempBrakeRate > BrakeRate)
4220  {
4221  BrakeRate = TempBrakeRate;
4222  }
4223  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4224  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
4225  if(HalfSpeedLimited)
4226  // this is the change referred to above
4227  {
4228  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
4229  ExitTimeHalf = EntryTime + BrakingTime;
4230  ExitTimeFull = ExitTimeHalf + BrakingTime;
4231  }
4232  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
4233  Utilities->CallLogPop(1095);
4234  return;
4235  }
4236  }
4237  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
4238  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
4239  // calc th, tf, sh, & sf
4240  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
4241  if(TempBrakeRate > MaxBrakeRate)
4242  {
4243  TempBrakeRate = MaxBrakeRate;
4244  }
4245  if(TempBrakeRate > BrakeRate)
4246  {
4247  BrakeRate = TempBrakeRate;
4248  }
4249  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4250  if(SignallerStopRequired)
4251  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
4252  {
4254  {
4256  // this prevents the brakerate from reducing for a signaller stop
4257  // regardless of other conditions that may change as progress round the loop
4258  }
4259  }
4261  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
4262  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
4263  {
4265  }
4266  int TempMaxExitSpeed;
4267  // calc current value & if less than MaxExitSpeed set that to this
4268  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
4269  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4270  {
4271  MaxExitSpeedAtHalfBraking = 0;
4272  }
4273  else
4274  {
4275  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4276  }
4277  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
4278  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
4279  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4280  {
4281  TempMaxExitSpeed = FrontElementMaxSpeed;
4282  }
4283  else
4284  {
4285  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4286  }
4287  if(TempMaxExitSpeed < MaxExitSpeed)
4288  {
4289  MaxExitSpeed = TempMaxExitSpeed;
4290  }
4291  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
4292  // Cumulativelength, and Cumulativelength
4293 
4294  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
4295  {
4296  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4297  if(ExitSpeedHalfSquared < 10)
4298  {
4299  ExitSpeedHalf = 0;
4300  }
4301  else
4302  {
4303  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4304  }
4305  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4306  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4307  if(ExitSpeedFullSquared < 10)
4308  {
4309  ExitSpeedFull = 0;
4310  }
4311  else
4312  {
4313  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4314  }
4315  if((StationFlag) && (CumulativeLength == FrontElementLength))
4316  {
4317  ExitSpeedFull = 0;
4318  // force a stop for station (not for buffers or red signal)
4319  }
4320  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4321  }
4322  // new condition at v2.4.0
4323  else if(PowerAtRail < 1)
4324  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4325  // avoid using AValue in denominator or have excessively long times
4326  {
4327  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4328  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4329  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4330 
4331  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4332  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4333  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4334  }
4335  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4336  // without the power need above condition or have hours of delay times, above added at v2.4.0
4337  {
4338  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4339  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4340  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4341  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4342  BrakeRate = 0;
4343  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4344  0.333334);
4345  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4346  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4348  // can accelerate continually over the half length
4349  {
4350  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4351  / 86400.0);
4353  // can accelerate continually over the full length
4354  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4355  {
4356  ExitTimeFull =
4357  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4358  }
4359  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4360  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4361  {
4362  // added at v0.6 as a safeguard
4363  if(MaxExitSpeed < EntrySpeed)
4364  {
4366  }
4367  // to prevent DeltaExitTimeToMaxInSecs being negative
4368  if(MaxExitSpeed < 1)
4369  {
4370  MaxExitSpeed = 1;
4371  }
4372  // to prevent divide by zero error
4373  // below as was
4375  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4376  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4377  (1.5 * AValue * AValue);
4378  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4379  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4380  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4381  }
4382  }
4383  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4384  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4385  // second halves of the element
4386  {
4387  // added at v0.6 as a safeguard
4388  if(MaxExitSpeed < EntrySpeed)
4389  {
4391  }
4392  // to prevent DeltaExitTimeToMaxInSecs being negative
4393  if(MaxExitSpeed < 1)
4394  {
4395  MaxExitSpeed = 1; // to prevent divide by zero error
4396  }
4397  // below as was
4399  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4400  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4401  (1.5 * AValue * AValue);
4402  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4403  // remaining distance to half length
4404  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4405  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4407  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4408  }
4409  }
4410  Utilities->CallLogPop(706);
4411  return;
4412  }
4413  else
4414  {
4415  if(!BuffersOrContinuationNowFlag)
4416  {
4417  if(NextSpeedLimit < LimitingSpeed)
4418  {
4419  LimitingSpeed = NextSpeedLimit;
4420  }
4421  }
4422  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4423  int TempMaxExitSpeed;
4424  // calc current value & if less than MaxExitSpeed set that to this
4425  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4426  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4427  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4428  {
4429  MaxExitSpeedAtHalfBraking = 0;
4430  }
4431  else
4432  {
4433  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4434  }
4435  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4436  {
4437  TempMaxExitSpeed = FrontElementMaxSpeed;
4438  }
4439  else
4440  {
4441  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4442  }
4443  if(TempMaxExitSpeed < MaxExitSpeed)
4444  {
4445  MaxExitSpeed = TempMaxExitSpeed;
4446  }
4447  // MaxExitSpeed is an external variable & this can reduce its value
4448  if(EntrySpeed > LimitingSpeed)
4449  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4450  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4451  {
4452  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4453  if(TempBrakeRate > MaxBrakeRate)
4454  {
4455  TempBrakeRate = MaxBrakeRate;
4456  }
4457  // shouldn't be for speedlimits since all known in advance, but include anyway
4458  if(TempBrakeRate > BrakeRate)
4459  {
4460  BrakeRate = TempBrakeRate;
4461  }
4462  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4463  }
4464  }
4465  if(!BuffersOrContinuationNowFlag)
4466  {
4467  CurrentTrackVectorPosition = NextTrackVectorPosition;
4468  EntryPos = NextEntryPos;
4469  CurrentElementHalfLength = NextElementHalfLength;
4470  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4471  {
4472  ContinuationNextFlag = true;
4473  }
4474  }
4475  }
4476  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4478  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4479  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4480  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4481  // stopping distance after it.
4482 
4483  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4484  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4485 
4486  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4487  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4488  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4489  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4490  // too late
4491 
4492  // set final braking or acc'n speed & time values
4493  if(BrakeRate > 0.01)
4494  {
4495  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4496  if(ExitSpeedHalfSquared < 10)
4497  {
4498  ExitSpeedHalf = 0;
4499  }
4500  else
4501  {
4502  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4503  }
4504  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4505  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4506  if(ExitSpeedFullSquared < 10)
4507  {
4508  ExitSpeedFull = 0;
4509  }
4510  else
4511  {
4512  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4513  }
4514  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4515  }
4516  else
4517  {
4518  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4519  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4520  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4521  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4522 
4523  BrakeRate = 0;
4524  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4525  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4526  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4528  {
4529  if(PowerAtRail > 1)
4530  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4531  {
4532  // [km/h/3.6 = m/s]
4533  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4534  / 86400.0);
4535  }
4536  else
4537  {
4538  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4539  }
4541  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4542  {
4543  if(PowerAtRail > 1)
4544  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4545  {
4546  // [km/h/3.6 = m/s]
4547  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4548  / 86400.0);
4549  }
4550  else
4551  {
4552  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4553  }
4554  }
4555  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4556  {
4557  // added at v0.6 as a safeguard
4558  if(MaxExitSpeed < EntrySpeed)
4559  {
4561  }
4562  // to prevent DeltaExitTimeToMaxInSecs being negative
4563  if(MaxExitSpeed < 1)
4564  {
4565  MaxExitSpeed = 1; // to prevent divide by zero error
4566  }
4567  // below as was
4569  double DeltaExitTimeToMaxInSecs;
4570  double DistanceToMax;
4571  if(PowerAtRail > 1) // added at v2.4.0
4572  {
4573  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4574  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4575  (1.5 * AValue * AValue);
4576  }
4577  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4578  {
4579  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4580  // these not really accurate but will be good enough
4581  DistanceToMax = EntryHalfLength;
4582  }
4583  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4584  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4585  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4586  }
4587  }
4588  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4589  {
4590  // added at v0.6 as a safeguard
4591  if(MaxExitSpeed < EntrySpeed)
4592  {
4594  }
4595  // to prevent DeltaExitTimeToMaxInSecs being negative
4596  if(MaxExitSpeed < 1)
4597  {
4598  MaxExitSpeed = 1; // to prevent divide by zero error
4599  }
4600  // below as was
4602  double DeltaExitTimeToMaxInSecs;
4603  double DistanceToMax;
4604  if(PowerAtRail > 1) // added at v2.4.0
4605  {
4606  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4607  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4608  (1.5 * AValue * AValue);
4609  }
4610  else
4611  {
4612  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4613  // these not really accurate but will be good enough
4614  DistanceToMax = EntryHalfLength / 2;
4615  }
4616  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4617  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4618  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4620  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4621  }
4622  }
4623  }
4624 
4625  else // SPADFlag set
4626  {
4628  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4629  if(ExitSpeedHalfSquared < 10)
4630  {
4631  ExitSpeedHalf = 0;
4632  }
4633  else
4634  {
4635  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4636  }
4637  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4638  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4639  if(ExitSpeedFullSquared < 10)
4640  {
4641  ExitSpeedFull = 0;
4642  }
4643  else
4644  {
4645  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4646  }
4647  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4648 
4649  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4650  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4651  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4652  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4653  // will be the stopping speed.
4654  if(ExitSpeedFull > 0)
4655  {
4656  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4657  {
4658  if((EntryPos == 0) || (EntryPos == 2))
4659  {
4660  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4661  {
4662  ExitPos = 1;
4663  }
4664  else
4665  {
4666  ExitPos = 3;
4667  }
4668  }
4669  else
4670  {
4671  ExitPos = 0;
4672  }
4673  }
4674  else
4675  {
4676  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4677  }
4678  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4679  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4680  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4681  {
4682  int NextElementLength;
4683  if(NextEntryPos > 1)
4684  {
4685  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4686  }
4687  else
4688  {
4689  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4690  }
4691  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4692  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4693  {
4694  ExitSpeedFull = 0;
4695  }
4696  }
4697  }
4698  }
4699  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4700  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4701  {
4702  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed <--NO,
4703  //change to BrakeRate = CoastingBrakeRate = 0.03 and calc times etc as normal - because of Albie Vowles' error report of 231223 where noticed
4704  //that failed train treated track lengths of > 2km as 100m so very noticeable. Keep going for exiting at continuation.
4705 
4706  //Coasting deceleration rate from paper 'Real-time train motion parameter estimation using an Unscented Kalman Filter' at
4707  //'https://www.sciencedirect.com/science/article/pii/S0968090X22002212'. In particular Fig 6 in section 4.3 shows coasting from 400sec to
4708  //1000sec corresponds to speed drop from 140km/h to 80km/h, i.e. 60km/h in 600sec, equivalent to 0.02777m/s/s deceleration, so use 0.03m/s/s.
4709 
4710  if(LeadElement > -1)
4711  {
4713  // don't stop on a continuation either entering or leaving
4714  {
4717  MaxExitSpeed = LimitingSpeed;
4718  BrakeRate = 0;
4719  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4720  // assume length is 50m for ease of calc
4721  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4722  Utilities->CallLogPop(2126);
4723  return;
4724  }
4725  }
4726  else if(MidElement > -1)
4727  {
4729  {
4732  MaxExitSpeed = LimitingSpeed;
4733  BrakeRate = 0;
4734  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4735  // assume length is 50m for ease of calc
4736  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4737  Utilities->CallLogPop(2127);
4738  return;
4739  }
4740  }
4741  else if(LagElement > -1)
4742  {
4744  {
4747  MaxExitSpeed = LimitingSpeed;
4748  BrakeRate = 0;
4749  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4750  // assume length is 50m for ease of calc
4751  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4752  Utilities->CallLogPop(2128);
4753  return;
4754  }
4755  }
4756 /* dropped at v2.18.1 in favour of CoastingBrakeRate which = 0.03m/s/s = see above explanation
4757  if(EntrySpeed > 7.5) // keep going for at least another element
4758  {
4759  ExitSpeedHalf = EntrySpeed - 2.5;
4760  ExitSpeedFull = EntrySpeed - 5;
4761  MaxExitSpeed = LimitingSpeed;
4762  BrakeRate = 0;
4763  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4764  // assume length is 50m for ease of calc
4765  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4766  Utilities->CallLogPop(2129);
4767  return;
4768  }
4769  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4770  {
4771  // will appear to have slowed at steady rate
4772  ExitSpeedHalf = 0;
4773  ExitSpeedFull = 0;
4774  MaxExitSpeed = LimitingSpeed;
4775  BrakeRate = 0;
4776  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4777  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4778  StoppedWithoutPower = true;
4779  Utilities->CallLogPop(2130);
4780  return;
4781  }
4782 */
4783  }
4784  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4785  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4786  Utilities->CallLogPop(707);
4787 }
4788 // ---------------------------------------------------------------------------
4789 /*
4790  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4791  {
4792  int NextExitPos;
4793  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4794  if(TimetableVector.empty()) return false;
4795  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4796  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4797  {
4798  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4799  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4800  {
4801  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4802  }
4803  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4804  {
4805  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4806  }
4807  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4808  NextElement = TempElement;
4809  }
4810  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4811  if(NextElement.TrackType == Buffers) return true;
4812  return false;//shouldn't reach here but include to prevent compiler return warning
4813  }
4814 */
4815 // ---------------------------------------------------------------------------
4816 
4817 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4818 /*
4819  returns the number by which the train ActionVectorEntryPtr needs
4820  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4821  actions when a stop or pass location has been reached before other timetabled events have been carried out. If can't find it, or Name
4822  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4823  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4824  or pass (false) the location.
4825 */{
4826  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4827  Stop = false;
4828  if(TimetableFinished || (Name == ""))
4829  {
4830  Utilities->CallLogPop(957);
4831  return(-1);
4832  }
4834 /*added the following check at v2.11.1 because of a fault found in Kevin Smith's railway (notified 02/01/22). CH01 started at
4835 Chester behind the stop position, but when it departed and this function was called it found Chester again with no cdt
4836 before it (it went round the Liverpool Loop), so it stopped again when it reached the stop position and reported all intermediate stops as having been
4837 missed. This check is for the train just having departed from the station in question, and if it has then any further stations
4838 with the same name are ignored - i.e. it stops a train from stopping at the same station twice in succession.
4839 */
4840  if(Ptr > &TrainDataEntryPtr->ActionVector.at(0))
4841  {
4842  Ptr--;
4843  if((Ptr->DepartureTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4844  {
4845  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4846  {
4847  Utilities->CallLogPop(2444);
4848  return(-1);
4849  }
4850  }
4851  }
4852  // start looking from current pointer position
4853  for(TActionVectorEntry *Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4854  {
4855  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4856  {
4857  break;
4858  }
4859  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4860  {
4861  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4862  {
4863  Stop = true;
4864  Utilities->CallLogPop(960);
4865  return (Ptr - ActionVectorEntryPtr);
4866  }
4867  }
4868  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4869  {
4870  Utilities->CallLogPop(1517);
4871  return (Ptr - ActionVectorEntryPtr);
4872  }
4873  }
4874  Utilities->CallLogPop(959);
4875  return(-1); // not found a valid entry
4876 }
4877 
4878 // ---------------------------------------------------------------------------
4879 
4881 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4882  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4883  Ignores the call-on signal.
4884 */{
4885  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4886  int ReturnVal = 0;
4887  int ElementCount = 0;
4888 /* dropped at v2.12.0 as takes up a great deal of time unnecessarily - substitute 1000 elements instead and return true (very unlikely to need to search this far [10km at min length])
4889  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4890  {
4891  Track->TrackElementAt(1031, x).TempTrackMarker01 = false;
4892  Track->TrackElementAt(1032, x).TempTrackMarker23 = false;
4893  }
4894 */
4895  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4896  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4897 
4898  while(true)
4899  {
4900  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4901  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4902  {
4903  ReturnVal = 1;
4904  break;
4905  }
4906  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4907  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4908  {
4909  ReturnVal = 2;
4910  break;
4911  }
4912  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && !Track->TrackElementAt(529,
4913  CurrentTrackVectorPosition).CallingOnSet && (LeadElement != CurrentTrackVectorPosition)) // CallingOnSet true when position lights lit,
4914  {//added LeadElement condition at v2.18.0 as train may be on the callon signal after CallOnSet false & don't want to return true for that
4915  ReturnVal = 3;
4916  break;
4917  }
4918 /* not needed at and after v2.12.0, see above
4919  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388, CurrentTrackVectorPosition).TrackType == Crossover))
4920  {
4921  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4922  // must be a loop - reached same point as examined earlier
4923  {
4924  ReturnVal = 4;
4925  break;
4926  }
4927  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4928  {
4929  ReturnVal = 4;
4930  break;
4931  }
4932  }
4933  else
4934  {
4935  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526, CurrentTrackVectorPosition).TempTrackMarker23))
4936  {
4937  ReturnVal = 4;
4938  break;
4939  }
4940  }
4941  if(EntryPos < 2)
4942  {
4943  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4944  }
4945  else
4946  {
4947  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4948  }
4949 */
4950 
4951  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4952  {
4953  if((EntryPos == 0) || (EntryPos == 2))
4954  {
4955  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4956  {
4957  ExitPos = 1;
4958  }
4959  else
4960  {
4961  ExitPos = 3;
4962  }
4963  }
4964  else
4965  {
4966  ExitPos = 0;
4967  }
4968  }
4969  else
4970  {
4971  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4972  }
4973  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4974  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4975  CurrentTrackVectorPosition = NextTrackVectorPosition;
4976  EntryPos = NextEntryPos;
4977  ElementCount++;
4978  if(ElementCount > 1000)
4979  {
4980  ReturnVal = 4;
4981  break;
4982  }
4983  }
4984  if(ReturnVal == 1)
4985  {
4986  Utilities->CallLogPop(708);
4987  return(false);
4988  }
4989  if(ReturnVal == 2)
4990  {
4991  Utilities->CallLogPop(709);
4992  return(true);
4993  }
4994  if(ReturnVal == 3)
4995  {
4996  Utilities->CallLogPop(946);
4997  return(true);
4998  }
4999  if(ReturnVal == 4)
5000  {
5001  Utilities->CallLogPop(947);
5002  return(true);
5003  }
5004  else
5005  {
5006  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
5007  }
5008 }
5009 
5010 // ---------------------------------------------------------------------------
5011 
5013 /*
5014  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
5015  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
5016  change of direction (cdt), remaining here (Frh), or under signaller control);
5017  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
5018  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
5019  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
5020  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] l) no existing route conflicts with the route into the platform,
5021  m) not failed or stopped without power
5022  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
5023  change points outside the route or have a route conflict if another route is set.
5024 */{
5025  if(Track->RouteFlashFlag || TrainFailed || StoppedWithoutPower) //failed & no power conditions added at v2.10.0
5026  {
5027  return(false); // don't want to create a new route from the stop signal if one is already in construction & can't call on if failed or no power
5028  }
5029  // some of the callingon route elements may be involved
5030  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
5031  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
5032  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
5033  int RouteStartPosition;
5034  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
5035  int PlatformPosition;
5036  // the track vector position of the first stop platfrom
5037  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
5038  // not used here
5039  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
5040  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
5041  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
5042  // must be stopped at a signal but not at a location & still in timetable (a)
5044  // no need to check for SignallerStopped as this function only called in Timetable mode
5045  {
5046  Utilities->CallLogPop(711);
5047  return(false);
5048  }
5049  while(true)
5050  {
5051  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
5052  // don't look further than 4km ahead (j)
5053  if(Distance > (4000 + LeadElementDistance))
5054  {
5055  Utilities->CallLogPop(967);
5056  return(false);
5057  }
5058  // if find another train on an element in front, before find a valid platform, return false (c)
5059  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
5060  {
5061  Utilities->CallLogPop(713);
5062  return(false);
5063  }
5064  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
5065  // be facing later on)
5066  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
5067  {
5068  // get LeadElement, if -1 return (could be exiting at continuation) (i)
5069  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
5070  if(OtherTrain.LeadElement == -1)
5071  {
5072  Utilities->CallLogPop(714);
5073  return(false);
5074  }
5075  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
5076  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
5077  {
5078  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
5079  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
5080  (OtherTrain.TrainMode == Signaller))
5081  {
5082  break;
5083  }
5084  else
5085  {
5086  Utilities->CallLogPop(955);
5087  return(false);
5088  }
5089  }
5090  else // (h)
5091  {
5092  break;
5093  }
5094  }
5095  // if reach buffers or exit continuation return false (can set route)
5096  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
5097  {
5098  Utilities->CallLogPop(716);
5099  return(false);
5100  }
5101  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
5102  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
5104  {
5105  Utilities->CallLogPop(717);
5106  return(false);
5107  }
5108  // if reach a location that isn't in timetable return false - drop this as still can't set a route
5109 /*
5110  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
5111  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
5112  {
5113  Utilities->CallLogPop(718);
5114  return false;
5115  }
5116 */
5117  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
5118  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
5119  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
5120  {
5121  if(StopRequired)
5122  {
5123  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
5124  {
5125  if(!PlatformFoundFlag)
5126  {
5127  PlatformPosition = CurrentTrackVectorPosition;
5128  }
5129  // ensure this only set once at first valid platform position - the unrestricted route will end here
5130  PlatformFoundFlag = true;
5131  }
5132  }
5133  }
5134  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
5135  // train has to be at station but that has to be before the next forward signal
5136 /*
5137  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
5138  {
5139  Utilities->CallLogPop(719);
5140  return false;
5141  }
5142 */
5143  // make sure points are followed correctly (d) & set ExitPos
5144  if(CurrentTrackElement.TrackType == Points)
5145  {
5146  if((EntryPos == 0) || (EntryPos == 2))
5147  {
5148  if(CurrentTrackElement.Attribute == 0)
5149  {
5150  ExitPos = 1;
5151  }
5152  else
5153  {
5154  ExitPos = 3;
5155  }
5156  }
5157  if(EntryPos == 1)
5158  {
5159  if(CurrentTrackElement.Attribute == 0)
5160  {
5161  ExitPos = 0;
5162  }
5163  else
5164  {
5165  Utilities->CallLogPop(720);
5166  return(false);
5167  }
5168  }
5169  if(EntryPos == 3)
5170  {
5171  if(CurrentTrackElement.Attribute == 1)
5172  {
5173  ExitPos = 0;
5174  }
5175  else
5176  {
5177  Utilities->CallLogPop(721);
5178  return(false);
5179  }
5180  }
5181  }
5182  else
5183  {
5184  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
5185  }
5186  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
5187  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
5188  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
5189  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
5190  if(ElementNumber < 2)
5191  {
5192  SkipRouteCheck = true;
5193  }
5194  else
5195  {
5196  SkipRouteCheck = false;
5197  }
5198  if(ElementNumber == 1) // the stop signal
5199  {
5200  RouteStartPosition = CurrentTrackVectorPosition;
5201  }
5202 /*
5203  if(ElementNumber == 2)
5204  {
5205  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
5206  else AutoSigs = false;
5207  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
5208  }
5209 */
5210  if(ElementNumber > 1)
5211  {
5212  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
5213  {
5214  RouteOrPartRouteSet = true;
5215  }
5216  else
5217  {
5218  RouteOrPartRouteSet = false;
5219  }
5220  }
5221  if(!SkipRouteCheck && !RouteOrPartRouteSet)
5222  {
5223  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
5224  {
5225  Utilities->CallLogPop(1859);
5226  return(false);
5227  }
5228  int ExitLink = CurrentTrackElement.Link[ExitPos];
5229  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
5230  {
5231  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
5232  {
5233  Utilities->CallLogPop(1850);
5234  return(false);
5235  }
5236  }
5237  }
5238  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
5239  if(EntryPos < 2)
5240  {
5241  Distance += CurrentTrackElement.Length01;
5242  }
5243  else
5244  {
5245  Distance += CurrentTrackElement.Length23;
5246  }
5247  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
5248  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
5249  CurrentTrackVectorPosition = NextTrackVectorPosition;
5250  EntryPos = NextEntryPos;
5251  ElementNumber++;
5252  } // while(true)
5253 
5254  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
5255  // from the stop signal (note that it may be last in an autosigs route)
5256  // a single element route at the stop signal should have been removed prior to this function being called (that called before
5257  // this in ClockTimer2)
5258 
5259  // now add elements to the CallonVector
5260  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
5261 
5262  AllRoutes->CallonVector.push_back(CallonEntry);
5263  Utilities->CallLogPop(1860);
5264  return(true); // return false if fail to set route for any reason
5265 }
5266 
5267 // ---------------------------------------------------------------------------
5268 /*
5269  bool TTrain::TimetableFinished()
5270  {
5271  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
5272  {
5273  return true;
5274  }
5275  return false;
5276  }
5277 */
5278 // ---------------------------------------------------------------------------
5279 
5280 AnsiString TTrain::GetTrainHeadCode(int Caller)
5281 
5282 {
5283  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
5284  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
5285 
5286  Utilities->CallLogPop(1452);
5287  return(RepeatHeadCode);
5288 }
5289 
5290 // ---------------------------------------------------------------------------
5291 
5292 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
5293 {
5294  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
5295  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
5296 
5297  Utilities->CallLogPop(1453);
5298  return(RepeatTime);
5299 }
5300 
5301 // ---------------------------------------------------------------------------
5302 
5303 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
5304 {
5305  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
5306  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
5307  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
5308  // first check that train is fully on the railway
5309  bool FrontValid = false, RearValid = false;
5310  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
5311 
5312  if((LeadElement == -1) || (MidElement == -1))
5313  {
5314  TrainToBeJoinedBy = NULL;
5315  Utilities->CallLogPop(2131);
5316  return(false);
5317  }
5319  {
5320  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
5321  FrontValid = true;
5322  }
5324  {
5325  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
5326  RearValid = true;
5327  }
5328  int TrainToBeJoinedByID = -1;
5329 
5330  // first check if on a 2-track element & select correct ID number if so
5331  if(FrontValid)
5332  {
5333  if(FrontAdjacentTrackElement.TrackType == Bridge)
5334  {
5336  {
5337  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5338  }
5339  else
5340  {
5341  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5342  }
5343  }
5344  else
5345  {
5346  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
5347  }
5348  }
5349  if((TrainToBeJoinedByID < 0) && RearValid)
5350  {
5351  // first check if on a 2-track element & select correct ID number if so
5352  if(RearAdjacentTrackElement.TrackType == Bridge)
5353  {
5355  {
5356  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5357  }
5358  else
5359  {
5360  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5361  }
5362  }
5363  else
5364  {
5365  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5366  }
5367  }
5368  if(TrainToBeJoinedByID < 0) // no adjacent train
5369  {
5370  TrainToBeJoinedBy = NULL;
5371  Utilities->CallLogPop(2132);
5372  return(false);
5373  }
5374  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5375  if(!TrainToBeJoinedBy->Stopped())
5376  {
5377  TrainToBeJoinedBy = NULL;
5378  Utilities->CallLogPop(2133);
5379  return(false);
5380  }
5381  Utilities->CallLogPop(2134);
5382  return(true);
5383 }
5384 
5385 // ---------------------------------------------------------------------------
5386 
5387 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, AnsiString SplitDistribution,
5388  TDateTime TimetableNonRepeatTime, bool Warning)
5389 /*
5390  Time = timetable time, the time adjustments for repeat trains is carried out internally
5391  Not all messages need this, if not needed a dummy value is required but not used
5392 
5393  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5394  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5395  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5396  //NB for Frh just give terminated message but without event time - don't use this function
5397  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5398  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5399  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5400  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5401  FrontSplit: 06:05:40: 2F46 split mass%-Power% = 10-50 from front to 3D54 at Old Street 1 minute late
5402  RearSplit: 06:05:40: 2F46 split mass%-Power% = 10-50 from rear to 3D54 at Old Street 1 minute late
5403  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5404  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5405  ChangeDescription: 06:05:40: 2F46 changed its description to 'NewDescription' at Old Street 1 minute late
5406  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5407  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5408  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5409  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5410  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5411  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5412  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5413  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5414  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass stop signal
5415  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5416  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5417  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5418  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5419  SignallerStop 06:05:40: 2F46 stopped on signaller command
5420  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5421  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5422 */{
5423  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5424  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5425  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5426  int IntMinsLate = 0;
5427 
5428  // need to set it in case MinsLate == 0, since it isn't tested for that
5429  if(ActionType == Arrive)
5430  {
5431  ActionLog = " arrived at ";
5432  }
5433  if(ActionType == Terminate)
5434  {
5435  if(TerminatedMessageSent) // to avoid it being sent twice
5436  {
5437  Utilities->CallLogPop(1104);
5438  return;
5439  }
5440  ActionLog = " terminated at ";
5441  TerminatedMessageSent = true;
5442  }
5443  if(ActionType == Depart)
5444  {
5445  ActionLog = " departed from ";
5446  }
5447  if(ActionType == Pass)
5448  {
5449  ActionLog = " passed ";
5450  }
5451  if(ActionType == Create)
5452  {
5453  ActionLog = " created at ";
5454  }
5455  if(ActionType == Enter)
5456  {
5457  ActionLog = " entered railway at ";
5458  }
5459  if(ActionType == ChangeDescription)
5460  {
5461  ActionLog = " changed its description to '" + Description + "' at "; //changed to train description at v2.16.1
5462  }
5463  if(ActionType == Leave)
5464  {
5465  ActionLog = " left railway at ";
5466  }
5467  if(ActionType == FrontSplit)
5468  {
5469  ActionLog = " split mass%-Power% = " + SplitDistribution + " from front to ";
5470  }
5471  if(ActionType == RearSplit)
5472  {
5473  ActionLog = " split mass%-Power% = " + SplitDistribution + " from rear to ";
5474  }
5475  if(ActionType == JoinedByOther)
5476  {
5477  ActionLog = " joined by ";
5478  }
5479  if(ActionType == ChangeDirection)
5480  {
5481  ActionLog = " changed direction at ";
5482  }
5483  if(ActionType == NewService)
5484  {
5485  ActionLog = " became new service ";
5486  }
5487  if(ActionType == TakeSignallerControl)
5488  {
5489  ActionLog = " taken under signaller control at ";
5490  }
5491  if(ActionType == RestoreTimetableControl)
5492  {
5493  ActionLog = " restored to timetable control at ";
5494  }
5495  if(ActionType == RemoveTrain)
5496  {
5497  if(Crashed)
5498  {
5499  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5500  }
5501  else if(Derailed)
5502  {
5503  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5504  }
5505  else
5506  {
5507  ActionLog = " REMOVED FROM RAILWAY at ";
5508  }
5509  }
5510  if(ActionType == SignallerMoveForwards)
5511  {
5512  ActionLog = " received signaller authority to proceed";
5513  }
5514  if(ActionType == SignallerStepForward)
5515  {
5516  ActionLog = " received signaller authority to step forward";
5517  }
5518  if(ActionType == SignallerChangeDirection)
5519  {
5520  ActionLog = " changed direction under signaller control at ";
5521  }
5522  if(ActionType == SignallerPassRedSignal)
5523  {
5524  ActionLog = " received signaller authority to pass stop signal";
5525  }
5526  if(ActionType == SignallerControlStop)
5527  {
5528  ActionLog = " received signaller instruction to stop";
5529  }
5530  if(ActionType == SignallerStop)
5531  {
5532  ActionLog = " stopped on signaller instruction ";
5533  }
5534  if(ActionType == SignallerJoin)
5535  {
5536  ActionLog = " joined under signaller control by ";
5537  }
5538  if(ActionType == TrainFailure)
5539  {
5540  ActionLog = " suffered an onboard power failure at ";
5541  }
5542  if(ActionType == RepairFailedTrain)
5543  {
5544  ActionLog = " failure repaired at ";
5545  }
5546  if(ActionType == SignallerLeave)
5547  {
5548  ActionLog = " left railway under signaller control at ";
5549  }
5550  if(OtherHeadCode != "")
5551  {
5552  OtherHeadCode += " at ";
5553  }
5554  TDateTime ActualTime = TrainController->TTClockTime;
5555 
5556  if(Warning)
5557  {
5558  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5559  WarningBaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName; //added time at v2.13.0
5560  }
5561  else
5562  {
5563  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5564  }
5565  bool TimePerformance = true;
5566 
5567  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5568  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5569  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5570  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5571  // SignallerJoin & RepairFailedTrain new at v2.4.0
5572  {
5573  TimePerformance = false;
5574  }
5575  if(TimePerformance)
5576  {
5577  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5578  MinsDelayed = float(MinsLate);
5579  if(ActionType == Pass) //added at v2.9.2 to prevent time to act increasing suddenly for early pass times then becoming 'NOW' when stops at signal
5580  {
5581  MinsDelayed = 0;
5582  }
5583  // new v2.2.0 for OpActionPanel, can be positive or negative
5584  if(ActionType == Arrive)
5585  {
5587  }
5588  // since train has just arrived this value is the
5589  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5590  // subtracted from later stop recoverable times.
5591  if(MinsLate < 0)
5592  {
5593  IntMinsLate = int(ceil(MinsLate));
5594  }
5595  if(MinsLate > 0)
5596  {
5597  IntMinsLate = int(floor(MinsLate));
5598  }
5599  if(IntMinsLate == 0)
5600  {
5601  PerfLog = " on time";
5602  }
5603  else if(IntMinsLate == 1)
5604  {
5605  PerfLog = " 1 minute late";
5606  }
5607  else if(IntMinsLate == -1)
5608  {
5609  PerfLog = " 1 minute early";
5610  }
5611  else if(IntMinsLate > 1)
5612  {
5613  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5614  }
5615  else if(IntMinsLate < -1)
5616  {
5617  int PosIntMinsLate = -IntMinsLate;
5618  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5619  }
5620  if(LocationName.Pos('-') > 0)
5621  {
5622  PerfLog = "," + PerfLog;
5623  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5624  }
5625  PerfLogForm->PerformanceLog(0, BaseLog + PerfLog);
5626  }
5627  else
5628  {
5629  PerfLogForm->PerformanceLog(1, BaseLog);
5630  }
5631  if(Warning)
5632  {
5633  Display->WarningLog(0, WarningBaseLog);
5634  }
5635  // update statistics
5636  if((ActionType == Arrive) && (IntMinsLate == 0))
5637  {
5639  }
5640  else if((ActionType == Arrive) && (IntMinsLate > 0))
5641  {
5643  TrainController->TotLateArrMins += IntMinsLate;
5644  }
5645  else if((ActionType == Arrive) && (IntMinsLate < 0))
5646  {
5648  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5649  }
5650 
5651  else if((ActionType == Pass) && (IntMinsLate == 0))
5652  {
5654  }
5655  else if((ActionType == Pass) && (IntMinsLate > 0))
5656  {
5658  TrainController->TotLatePassMins += IntMinsLate;
5659  }
5660  else if((ActionType == Pass) && (IntMinsLate < 0))
5661  {
5663  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5664  }
5665 
5666  else if((ActionType == Leave) && (IntMinsLate == 0)) //new at v2.9.1 as had been omitted in error earlier
5667  {
5669  }
5670  else if((ActionType == Leave) && (IntMinsLate > 0))
5671  {
5673  TrainController->TotLateExitMins += IntMinsLate;
5674  }
5675  else if((ActionType == Leave) && (IntMinsLate < 0))
5676  {
5678  TrainController->TotEarlyExitMins += abs(IntMinsLate);
5679  }
5680 
5681  else if((ActionType == Depart) && (IntMinsLate == 0)) //can't depart early
5682  {
5684  }
5685  else if((ActionType == Depart) && (IntMinsLate > 0))
5686  {
5688  TrainController->TotLateDepMins += IntMinsLate;
5689  }
5690  Utilities->CallLogPop(968);
5691 }
5692 
5693 // ---------------------------------------------------------------------------
5694 
5695 void TTrain::TrainHasFailed(int Caller)
5696 {
5697  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5698  if(Crashed || Derailed || DerailPending)
5699  {
5700  TrainFailurePending = false;
5701  Utilities->CallLogPop(2135);
5702  return;
5703  }
5704  AnsiString LocName = "";
5705 
5706  if(LeadElement > -1)
5707  {
5709  }
5710  if((LocName == "") && (MidElement > -1))
5711  {
5713  }
5714  if((LocName == "") && LeadElement > -1)
5715  {
5716  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5717  }
5718  if((LocName == "") && (MidElement > -1))
5719  {
5720  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5721  }
5722  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5723  TrainFailed = true;
5724  TrainFailurePending = false;
5725  CallingOnFlag = false; //added at v2.10.0
5727  PowerAtRail = 0.08;
5728  AValue = sqrt(2 * PowerAtRail / Mass);
5730  // TrainFailed only called when PlotElements properly set to Lead, Mid & Lag elements
5731  if(Stopped())
5732  {
5733  EntrySpeed = 0;
5734  ExitSpeedHalf = 0;
5735  ExitSpeedFull = 0;
5736  MaxExitSpeed = 0;
5737  BrakeRate = 0;
5738  StoppedWithoutPower = true;
5739  }
5741  LogAction(33, HeadCode, "", TrainFailure, LocName, "", TDateTime(0), true);
5742  // true for warning, TDateTime not used
5743  Utilities->CallLogPop(2136);
5744 }
5745 
5746 // ---------------------------------------------------------------------------
5747 
5748 void TTrain::FrontTrainSplit(int Caller) //Major rewrite at v2.18.0 using new ThisLocationLongEnoughForSplit
5749 {
5750 /*
5751  Split logic for is:- the final 4 train positions must overlap with the original train, & final 4 positions
5752  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5753  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5754  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5755 */
5756  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5757  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5758  if(PowerAtRail < 1)
5759  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5760  {
5762  {
5763  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5764  }
5766  Utilities->CallLogPop(2137);
5767  return;
5768  }
5769  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5770 
5771  if(LocationName == "")
5772  {
5773  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5774  }
5775  int RearTrainRearPos, RearTrainFrontPos, RearTrainExitPos;
5776  int FrontTrainRearPos, FrontTrainFrontPos;
5778 
5779  if(LocationName == "")
5780  {
5781  throw Exception("Error - LocationName not set in FrontTrainSplit");
5782  }
5783  // if message given call at ~5 sec intervals in case train repositioned
5784 
5785  bool TemporaryDelay = false;
5787  MidEntryPos, FrontTrainFrontPos, FrontTrainRearPos, RearTrainFrontPos, RearTrainRearPos, TemporaryDelay))
5788  {
5790  {
5792  {
5793  TrainController->StopTTClockMessage(151, HeadCode + " failed to split - location too short at " + LocationName + ", reposition train if possible.");
5794  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5796  }
5797  Utilities->CallLogPop(1009);
5798  return;
5799  }
5800  }
5801 
5802  if(TemporaryDelay)
5803  {
5805  Utilities->CallLogPop(2683);
5806  return;
5807  }
5808 
5809 //it is long enough for split
5810  AnsiString SplitTrainFixedDescription = ActionVectorEntryPtr->LinkedTrainEntryPtr->FixedDescription; //save these for new train before ActionVectorEntryPtr incremented
5811  AnsiString SplittingTrainDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
5812  bool SplitTrainExplicitDescription = ActionVectorEntryPtr->LinkedTrainEntryPtr->ExplicitDescription;
5814 
5815  UnplotTrain(0);
5816  StartSpeed = 0;
5817  RearStartElement = RearTrainRearPos;
5818  for(int x = 0; x < 4; x++)
5819  {
5820  if(Track->TrackElementAt(1664, RearStartElement).Conn[x] == RearTrainFrontPos)
5821  {
5822  RearStartExitPos = x;
5823  }
5824  }
5825  StoppedAtLocation = true;
5826  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5827  {
5828  StoppedWithoutPower = true;
5829  }
5830  PlotStartPosition(3);
5833 // ActionVectorEntryPtr++; moved lower down at v2.15.0 because of new section below
5835 
5836  //new at v2.15.0 for unequal split in mass & power
5837  int NewTrainMass;
5838  double NewTrainPowerAtRail;
5840  {
5841  int pos = ActionVectorEntryPtr->SplitDistribution.Pos('-');
5842  int MassPercent = ActionVectorEntryPtr->SplitDistribution.SubString(1, pos - 1).ToInt(); //validity checked during validation
5843  int PowerPercent = ActionVectorEntryPtr->SplitDistribution.SubString(pos + 1, ActionVectorEntryPtr->SplitDistribution.Length() - pos).ToInt();
5844  NewTrainMass = Mass * double(MassPercent)/100.0;
5845  Mass = Mass - NewTrainMass;
5846  NewTrainPowerAtRail = PowerAtRail * double(PowerPercent)/100.0;
5847  if(NewTrainPowerAtRail == 0)
5848  {
5849  NewTrainPowerAtRail = 0.08; //min value represents 0
5850  }
5851  PowerAtRail = PowerAtRail - NewTrainPowerAtRail;
5852  AValue = sqrt(2 * PowerAtRail / Mass);
5853  }
5854  else // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5855  {
5856  Mass = Mass / 2;
5857  NewTrainMass = Mass;
5858  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
5859  // MaxBrakeRate = MaxBrakeRate / 2; this was wrong - want brake rate to stay the same, brake force is halved but that not a train parameter
5860  // and when needed it's calculated from rate & mass - changed at v2.15.0
5861  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
5862  PowerAtRail = PowerAtRail / 2;
5863  NewTrainPowerAtRail = PowerAtRail;
5864  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
5865  AValue = sqrt(2 * PowerAtRail / Mass);
5866  // shouldn't change but include in case not set earlier
5867  }
5868 
5869  TActionEventType EventType = NoEvent;
5870  ActionVectorEntryPtr++; //moved here at v2.18.0 to give more chances to split in case points set wrongly initially, also when AddTrain TrainVector
5871  //may be repositioned so all references to this train may be invalid
5872 
5873  if(!TrainController->AddTrain(0, FrontTrainRearPos, FrontTrainFrontPos, OtherHeadCode, 0, NewTrainMass, MaxRunningSpeed, MaxBrakeRate, NewTrainPowerAtRail,
5874  "Timetable", LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5875  // false for SignallerControl
5876  {
5877  Utilities->CallLogPop(1721); // EventType not used here
5878  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5879  // another train, in which case a message will have been sent to the perf log, also might well clear later
5880  // when other train moves away
5881  return;
5882  }
5883 
5884  TrainController->TrainVector.back().Description = SplitTrainFixedDescription; //added at v2.16.1, new train takes description from its TrainDataEntry
5885  if(!SplitTrainExplicitDescription) //new at v2.15.0 see above
5886  {
5887 // OldActionVectorEntryPtr->LinkedTrainEntryPtr->Description = OriginalDescription; dropped at v2.16.1
5888  TrainController->TrainVector.back().Description = SplittingTrainDescription; //else takes it from this train's description
5889  }
5890 
5891  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5892  // see mods in UpdateTrain for v1.3.2
5893  TrainController->TrainAdded = true;
5894 
5895  TTrainOperatingData &TTOD = LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5896 
5897  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5898  TTOD.RunningEntry = Running;
5899  Utilities->CallLogPop(998);
5900 }
5901 
5902 // ---------------------------------------------------------------------------
5903 
5904 void TTrain::RearTrainSplit(int Caller) //Major rewrite at v2.18.0 using new ThisLocationLongEnoughForSplit
5905 {
5906 /*
5907  Split logic for is:- the final 4 train positions must overlap with the original train, & final 4 positions
5908  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5909  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5910  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5911 */
5912  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5913  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5914  if(PowerAtRail < 1)
5915  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5916  {
5918  {
5919  TrainController->StopTTClockMessage(176, HeadCode + ": A train without power can't split");
5920  }
5922  Utilities->CallLogPop(2685);
5923  return;
5924  }
5925  AnsiString LocationName = Track->TrackElementAt(1676, LeadElement).ActiveTrackElementName;
5926 
5927  if(LocationName == "")
5928  {
5929  LocationName = Track->TrackElementAt(1677, MidElement).ActiveTrackElementName;
5930  }
5931  int RearTrainRearPos, RearTrainFrontPos;
5932  int FrontTrainRearPos, FrontTrainFrontPos, FrontTrainExitPos;
5934 
5935  if(LocationName == "")
5936  {
5937  throw Exception("Error - LocationName not set in FrontTrainSplit");
5938  }
5939  // if message given call at ~5 sec intervals in case train repositioned
5940 
5941  bool TemporaryDelay = false;
5943  MidEntryPos, FrontTrainFrontPos, FrontTrainRearPos, RearTrainFrontPos, RearTrainRearPos, TemporaryDelay))
5944  {
5946  {
5948  {
5949  TrainController->StopTTClockMessage(177, HeadCode + " failed to split - location too short at " + LocationName + ", reposition train if possible.");
5950  TrainController->LogActionError(66, HeadCode, "", FailLocTooShort, LocationName);
5952  }
5953  Utilities->CallLogPop(2686);
5954  return;
5955  }
5956  }
5957 
5958  if(TemporaryDelay)
5959  {
5961  Utilities->CallLogPop(2684);
5962  return;
5963  }
5964 
5965 //it is long enough for split
5966  AnsiString SplitTrainFixedDescription = ActionVectorEntryPtr->LinkedTrainEntryPtr->FixedDescription; //save these for new train before ActionVectorEntryPtr incremented
5967  AnsiString SplittingTrainDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
5968  bool SplitTrainExplicitDescription = ActionVectorEntryPtr->LinkedTrainEntryPtr->ExplicitDescription;
5970 
5971  UnplotTrain(11);
5972  StartSpeed = 0;
5973  RearStartElement = FrontTrainRearPos;
5974  for(int x = 0; x < 4; x++)
5975  {
5976  if(Track->TrackElementAt(1665, RearStartElement).Conn[x] == FrontTrainFrontPos)
5977  {
5978  RearStartExitPos = x;
5979  }
5980  }
5981  StoppedAtLocation = true;
5982  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5983  {
5984  StoppedWithoutPower = true;
5985  }
5986  PlotStartPosition(12);
5989 // ActionVectorEntryPtr++; moved lower down at v2.15.0 because of new section below
5991 
5992  //new at v2.15.0 for unequal split in mass & power
5993  int NewTrainMass;
5994  double NewTrainPowerAtRail;
5996  {
5997  int pos = ActionVectorEntryPtr->SplitDistribution.Pos('-');
5998  int MassPercent = ActionVectorEntryPtr->SplitDistribution.SubString(1, pos - 1).ToInt(); //validity checked during validation
5999  int PowerPercent = ActionVectorEntryPtr->SplitDistribution.SubString(pos + 1, ActionVectorEntryPtr->SplitDistribution.Length() - pos).ToInt();
6000  NewTrainMass = Mass * double(MassPercent)/100.0;
6001  Mass = Mass - NewTrainMass;
6002  NewTrainPowerAtRail = PowerAtRail * double(PowerPercent)/100.0;
6003  if(NewTrainPowerAtRail == 0)
6004  {
6005  NewTrainPowerAtRail = 0.08; //min value represents 0
6006  }
6007  PowerAtRail = PowerAtRail - NewTrainPowerAtRail;
6008  AValue = sqrt(2 * PowerAtRail / Mass);
6009  }
6010  else // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
6011  {
6012  Mass = Mass / 2;
6013  NewTrainMass = Mass;
6014  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
6015  // MaxBrakeRate = MaxBrakeRate / 2; this was wrong - want brake rate to stay the same, brake force is halved but that not a train parameter
6016  // and when needed it's calculated from rate & mass - changed at v2.15.0
6017  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
6018  PowerAtRail = PowerAtRail / 2;
6019  NewTrainPowerAtRail = PowerAtRail;
6020  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
6021  AValue = sqrt(2 * PowerAtRail / Mass);
6022  // shouldn't change but include in case not set earlier
6023  }
6024 
6025  TActionEventType EventType = NoEvent;
6026  ActionVectorEntryPtr++; //moved here at v2.18.0 to give more chances to split in case points set wrongly initially, also when AddTrain TrainVector
6027  //may be repositioned so all references to this train may be invalid
6028 
6029  if(!TrainController->AddTrain(4, RearTrainRearPos, RearTrainFrontPos, OtherHeadCode, 0, NewTrainMass, MaxRunningSpeed, MaxBrakeRate, NewTrainPowerAtRail,
6030  "Timetable", LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
6031  // false for SignallerControl
6032  {
6033  Utilities->CallLogPop(2687); // EventType not used here
6034  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
6035  // another train, in which case a message will have been sent to the perf log, also might well clear later
6036  // when other train moves away
6037  return;
6038  }
6039 
6040  TrainController->TrainVector.back().Description = SplitTrainFixedDescription; //added at v2.16.1, new train takes description from its TrainDataEntry
6041  if(!SplitTrainExplicitDescription) //new at v2.15.0 see above
6042  {
6043 // OldActionVectorEntryPtr->LinkedTrainEntryPtr->Description = OriginalDescription; dropped at v2.16.1
6044  TrainController->TrainVector.back().Description = SplittingTrainDescription; //else takes it from this train's description
6045  }
6046 
6047  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
6048  // see mods in UpdateTrain for v1.3.2
6049  TrainController->TrainAdded = true;
6050 
6051  TTrainOperatingData &TTOD = LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6052 
6053  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6054  TTOD.RunningEntry = Running;
6055  Utilities->CallLogPop(2688);
6056 }
6057 
6058 // ---------------------------------------------------------------------------
6059 
6060 void TTrain::FinishJoin(int Caller)
6061 {
6062  if(FinishJoinLogSent == false)
6063  {
6064  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6065  FinishJoinLogSent = true; // so don't keep logging this event
6066  // don't need to reset it to false after the event as the train is deleted
6067  }
6068  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6069  if(TrainFailed)
6070  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
6071  {
6073  {
6074  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6075  }
6077  Utilities->CallLogPop(2139);
6078  return;
6079  }
6080  if(TrainGone)
6081  // this means that the train has already joined the other & is awaiting deletion by TrainController
6082  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6083  // on from jbo & TrainToJoinIsAdjacent returns false
6084  {
6085  Utilities->CallLogPop(1035);
6086  return;
6087  }
6088  TTrain *TrainToJoin;
6090 
6091  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6092  {
6094  {
6095  // PerfLogForm->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6098  }
6099  Utilities->CallLogPop(1030);
6100  return; // keep this here in case need to add code before final return
6101  }
6102  // no need to clear error report flag here, cleared in jbo function
6103  // No need to set TimetableFinished, done in jbo function
6104  Utilities->CallLogPop(1031);
6105 }
6106 
6107 // ---------------------------------------------------------------------------
6108 
6109 void TTrain::JoinedBy(int Caller)
6110 {
6111  if(TrainController->OpTimeToActUpdateCounter == 0) //added at v2.13.2. Use OpTimeToActUpdateCounter for convenience so only issue the event log
6112  //once every second rather than many times. Can't use an event logged flag because there may
6113  //be several trains that are to be joined by others
6114  {
6115  TrainController->LogEvent("" + AnsiString(Caller) + "," + HeadCode + ",Waiting to be joined");
6116  }
6117  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6118  if(PowerAtRail < 1)
6119  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6120  {
6122  {
6123  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6124  }
6126  Utilities->CallLogPop(2140);
6127  return;
6128  }
6129  TTrain *TrainToBeJoinedBy;
6131 
6132  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6133  {
6135  {
6136  // PerfLogForm->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6139  }
6140  LastActionDelayFlag = true;
6141  // need to update LastActionTime if this train first to arrive as need 30s after
6142  // both adjacent before the join
6143  Utilities->CallLogPop(1032);
6144  return;
6145  }
6146  // here when other train is adjacent
6148  {
6150  // need to update this as need 30s after both adjacent before the join
6151  LastActionDelayFlag = false;
6152  Utilities->CallLogPop(1033);
6153  return;
6154  }
6155  // here when other train is adjacent & 30 secs elapsed since both adjacent
6156 
6157  // set new values for mass etc
6158  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6159  {
6160  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6161  }
6162  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6163  double OwnBrakeForce = MaxBrakeRate * Mass;
6164  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6165 
6166  Mass += TrainToBeJoinedBy->Mass;
6167  MaxBrakeRate = CombinedBrakeRate;
6168  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6169  AValue = sqrt(2 * PowerAtRail / Mass);
6170 
6172  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6173  TrainToBeJoinedBy->TimetableFinished = true;
6174  TrainToBeJoinedBy->TrainGone = true;
6175  TrainController->LogEvent("" + AnsiString(Caller) + "," + HeadCode + ",Joined By," + FJOHeadCode); //added at v2.13.2 to provide more information
6176  // this will cause other train to be deleted
6177  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6181  Utilities->CallLogPop(1034);
6182 }
6183 
6184 // ---------------------------------------------------------------------------
6185 
6186 void TTrain::ChangeTrainDirection(int Caller, bool NoLogFlag)
6187 {
6188  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6189  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6190  if(PowerAtRail < 1)
6191  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6192  {
6194  {
6195  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6196  }
6197  ZeroPowerNoCDTMessage = true;
6198  Utilities->CallLogPop(2141);
6199  return;
6200  }
6201  TColor TempColour = BackgroundColour;
6202 
6203  UnplotTrain(2);
6206  StartSpeed = 0;
6207  StoppedAtLocation = true;
6208  PlotStartPosition(1);
6209  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6210  // plot same as was - should always be pale green
6211  if(!NoLogFlag)
6212  {
6215  }
6217 
6218  //now erase a stub route if there is one, added at v2.5.1
6219  //first element of route is now immediately behind the train (i.e. next to MidElement)
6220  if(MidEntryPos >= 0)
6221  {
6222  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6223  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6224  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6225  int RouteNumber = -1;
6226  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6227  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6228  {
6229  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6230  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
6231  //elements can continue to be removed from that route
6232  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6233 // if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6234  { //above condition removed v2.17.0 so non-facing signal or continuation doesn't stop route being removed
6235  //if it is a facing signal then it will be detected below and not removed
6236  bool FirstPass = true; //added at v2.8.0
6237  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6238  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
6239  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6240  int TVPos2 = PDE.GetTrackVectorPosition();
6241  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6242  {
6243  break;
6244  }
6245  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6247  {
6248  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6249  }
6250  else
6251  {
6252  break;
6253  }
6254  FirstPass = false;
6255  }
6256  AllRoutes->RebuildRailwayFlag = true;
6257  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6258  }
6259  }
6260  }
6261  Utilities->CallLogPop(1012);
6262 }
6263 
6264 // ---------------------------------------------------------------------------
6265 
6266 void TTrain::NewTrainService(int Caller, bool NoLogFlag) //, bool NoLogFlag added at v2.12.0 for new service tt skips
6267 // change to new train, give new service message
6268 //same RepeatNumber used for the new service
6269 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6270  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6271  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6272  if(PowerAtRail < 1)
6273  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6274  {
6276  {
6277  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6278  }
6280  Utilities->CallLogPop(2142);
6281  return;
6282  }
6284 
6285  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6286 
6287  if(!NoLogFlag)
6288  {
6290  }
6291  UnplotTrain(3);
6294  StartSpeed = 0;
6299  HeadCode = NewHeadCode;
6301  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6302  {
6303  Description = OriginalDescription; //changed at v2.16.1 to train description
6304  }
6305  StoppedAtLocation = true;
6306  PlotStartPosition(5);
6308  // pale green
6311  TerminatedMessageSent = false;
6312  Utilities->CallLogPop(1022);
6313 }
6314 
6315 // ---------------------------------------------------------------------------
6316 
6317 void TTrain::RemainHere(int Caller)
6318 {
6319  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6320  if(RemainHereLogNotSent) // to prevent repeated logs
6321  {
6322  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6323  RemainHereLogNotSent = false;
6324  }
6326  {
6330  TerminatedMessageSent = true;
6331  }
6332  TimetableFinished = true;
6333  Utilities->CallLogPop(1023);
6334 }
6335 
6336 // ---------------------------------------------------------------------------
6337 
6338 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6339 /*
6340  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6341  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6342  except where an action is a departure, starting at the current value for the pointer
6343  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6344  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6345 */{
6346  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6347  {
6348  return; // if remove train that starts under signaller control no messages needed
6349 
6350  }
6351  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6352  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6353  if(IncNum > 0)
6354  {
6355  for(int x = 0; x < IncNum; x++)
6356  {
6357  if(x > 0)
6358  {
6359  Ptr++;
6360  }
6361  // arrival - no need to test for termination as this section only covers missed actions up to the
6362  // arrival point - may terminate later but that not missed
6363  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6364  {
6366  }
6367  // arrival & departure
6368  if(Ptr->FormatType == TimeTimeLoc)
6369  {
6371  }
6372  // departure
6373  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6374  {
6375  continue; // skip TimeLoc departures, message given for arrivals
6376  }
6377  // pass
6378  else if(Ptr->FormatType == PassTime)
6379  {
6381  }
6382  // split
6383  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6384  {
6386  }
6387  // jbo
6388  else if(Ptr->Command == "jbo")
6389  {
6391  }
6392  // dsc
6393  else if(Ptr->Command == "dsc") //new at v2.15.0
6394  {
6396  }
6397  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6398  // be starts, finishes or cdt
6399  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6400  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6401  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6402  (Ptr->FormatType == Repeat))
6403  {
6404  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6405  }
6406  }
6407  }
6408  else
6409  {
6410  bool IncludeFER = false;
6411  if(IncNum == -1)
6412  {
6413  IncludeFER = true;
6414  }
6415  while(true) // finish commands & repeats break out of loop
6416  {
6417  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6418  if(!IncludeFER && (Ptr->Command == "Fer"))
6419  {
6420  break;
6421  }
6422  // Fer & included
6423  else if(IncludeFER && (Ptr->Command == "Fer"))
6424  {
6426  break;
6427  }
6428  // Repeat
6429  else if(Ptr->FormatType == Repeat)
6430  {
6431  break;
6432  }
6433  // Fjo
6434  else if(Ptr->Command == "Fjo")
6435  {
6437  break;
6438  }
6439  // Frh
6440  else if(Ptr->Command == "Frh")
6441  {
6443  {
6445  TerminatedMessageSent = true;
6446  }
6447  break;
6448  }
6449  // Frh-sh
6450  else if(Ptr->Command == "Frh-sh")
6451  {
6453  {
6455  TerminatedMessageSent = true;
6456  }
6457  break;
6458  }
6459  // Fns, F-nshs, Fns-sh
6460  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6461  {
6463  break;
6464  }
6465  // end of breakout actions
6466  // arrival
6467  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6468  {
6469  if(IsTrainTerminating(1))
6470  {
6472  TerminatedMessageSent = true;
6473  }
6474  else
6475  {
6477  }
6478  }
6479  // arrival & departure
6480  else if(Ptr->FormatType == TimeTimeLoc)
6481  {
6483  }
6484  // departure
6485  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6486  {
6487  Ptr++;
6488  continue; // skip TimeLoc departures, message given for arrivals
6489  }
6490  // pass
6491  else if(Ptr->FormatType == PassTime)
6492  {
6494  }
6495  // split
6496  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6497  {
6499  }
6500  // jbo
6501  else if(Ptr->Command == "jbo")
6502  {
6504  }
6505  // dsc
6506  else if(Ptr->Command == "dsc") //new at v2.15.0
6507  {
6508 // TrainController->LogActionError(65, HeadCode, "", FailMissedDSC, Ptr->LocationName); don't count as a missed event
6509  }
6510  // cdt
6511  else if(Ptr->Command == "cdt")
6512  {
6513 // TrainController->LogActionError(25, HeadCode, "", FailMissedChangeDirection, Ptr->LocationName); //commented out at v2.12.0 as cdts not counted as missed events
6514  }
6515  // Errors
6516  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6517  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6518  {
6519  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6520  }
6521  Ptr++;
6522  }
6523  TimetableFinished = true;
6524  }
6525  Utilities->CallLogPop(1021);
6526 }
6527 
6528 // ---------------------------------------------------------------------------
6529 
6530 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6531 // ensure same repeatnumber
6532 {
6533  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6535 
6536  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6537  {
6538  Utilities->CallLogPop(1024);
6539  return(false);
6540  }
6541  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6542  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6543  {
6544  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6545  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6546  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6547  {
6548  Utilities->CallLogPop(1025);
6549  return(true);
6550  }
6551  }
6552  Utilities->CallLogPop(1026);
6553  return(false);
6554 }
6555 
6556 // ---------------------------------------------------------------------------
6557 
6558 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6559 // ensure same repeatnumber
6560 {
6561  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6562  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6563 
6564  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6565  {
6566  Utilities->CallLogPop(1027);
6567  return(false);
6568  }
6569  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6570  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6571  {
6572  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6573  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6574  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6575  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6576  {
6577  Utilities->CallLogPop(1028);
6578  return(true);
6579  }
6580  }
6581  Utilities->CallLogPop(1029);
6582  return(false);
6583 }
6584 
6585 // ---------------------------------------------------------------------------
6586 
6587 void TTrain::NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6588 { //same RepeatNumber (i.e. 0) used for the new shuttle
6589 //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6590  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6591  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6592  if(PowerAtRail < 1)
6593  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6594  {
6596  {
6597  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6598  }
6600  Utilities->CallLogPop(2143);
6601  return;
6602  }
6603  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6604  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6605 
6606  if(!NoLogFlag)
6607  {
6609  }
6610  UnplotTrain(4);
6613  StartSpeed = 0;
6618  HeadCode = NewHeadCode;
6620  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6621  {
6622  Description = OriginalDescription; //changed at v2.16.1 to train description
6623  }
6624  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6625  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6626  StoppedAtLocation = true;
6627  PlotStartPosition(6);
6629  // pale green
6632  TerminatedMessageSent = false;
6633  Utilities->CallLogPop(1078);
6634 }
6635 
6636 // ---------------------------------------------------------------------------
6637 
6638 void TTrain::RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6639 // need to check whether all repeats finished or not
6640 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6641  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6642  if(RemainHereLogNotSent) // to prevent repeated logs
6643  {
6644  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6645  RemainHereLogNotSent = false;
6646  }
6648  // finished all repeats
6649  {
6651  { //no need to suppress this LogAction because BecomeNewService won't be available in this case
6654  TerminatedMessageSent = true;
6655  // no need to clear message as no more actions
6656  }
6657  TimetableFinished = true;
6658  Utilities->CallLogPop(1080);
6659  return;
6660  }
6661  if(PowerAtRail < 1)
6662  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6663  {
6665  {
6666  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6667  }
6669  Utilities->CallLogPop(2144);
6670  return;
6671  }
6672  int TempRepeatNumber = RepeatNumber + 1;
6673  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6674  // until after LogAction or the wrong time will be used
6675  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6676  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6677 
6678  if(!NoLogFlag)
6679  {
6681  }
6682  RepeatNumber++;
6683  UnplotTrain(5);
6686  StartSpeed = 0;
6691  HeadCode = NewHeadCode;
6693  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6694  {
6695  Description = OriginalDescription; //changed at v2.16.1 to train description
6696  }
6697  StoppedAtLocation = true;
6698  PlotStartPosition(7);
6700  // pale green
6703  TerminatedMessageSent = false;
6704  Utilities->CallLogPop(1079);
6705 }
6706 
6707 // ---------------------------------------------------------------------------
6708 
6709 void TTrain::RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips
6710 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6711  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6712  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6713  if(PowerAtRail < 1)
6714  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6715  {
6717  {
6718  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6719  }
6721  Utilities->CallLogPop(2145);
6722  return;
6723  }
6725  // finished all repeats
6726  {
6727  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6728  if(!NoLogFlag)
6729  {
6731  }
6732  RepeatNumber = 0;
6733  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6734  UnplotTrain(6);
6737  StartSpeed = 0;
6739  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6740  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6742  HeadCode = NewHeadCode;
6744  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6745  {
6746  Description = OriginalDescription; //changed at v2.16.1 to train description
6747  }
6748  StoppedAtLocation = true;
6749  PlotStartPosition(9);
6753  TerminatedMessageSent = false;
6754  Utilities->CallLogPop(1081);
6755  return;
6756  }
6757  int TempRepeatNumber = RepeatNumber + 1;
6758  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6759  // until after LogAction or the wrong time will be used
6760  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6761  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6762 
6763  if(!NoLogFlag)
6764  {
6766  }
6767  RepeatNumber++;
6768  UnplotTrain(7);
6771  StartSpeed = 0;
6776  HeadCode = NewHeadCode;
6778  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6779  {
6780  Description = OriginalDescription; //changed at v2.16.1 to train description
6781  }
6782  StoppedAtLocation = true;
6783  PlotStartPosition(8);
6785  // pale green
6788  TerminatedMessageSent = false;
6789  Utilities->CallLogPop(1082);
6790 }
6791 
6792 // ---------------------------------------------------------------------------
6793 
6795 {
6796  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6797  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6798  // must be preceded by a TimeLoc departure
6799  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6800  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6801  {
6803  {
6804  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6805  {
6806  Utilities->CallLogPop(1083);
6807  return(false);
6808  }
6809  else if((ActionVectorEntryPtr + x)->SequenceType == FinishSequence)
6810  {
6811  Utilities->CallLogPop(1084);
6812  return(true);
6813  }
6814  }
6815  }
6816  Utilities->CallLogPop(1085);
6817  return(false);
6818 }
6819 
6820 // ---------------------------------------------------------------------------
6821 
6822 bool TTrain::AbleToMove(int Caller)
6823 {
6824  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6825  bool Able = true;
6826 
6827  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0
6828  {
6829  // StoppedForTrainInFront removed as tested below
6830  Utilities->CallLogPop(2146); // added v2.4.0
6831  return(false); // added v2.4.0
6832  }
6833  if(LeadElement > -1)
6834  {
6835  if(Track->TrackElementAt(801, LeadElement).TrackType == Buffers) //moved up here from 'else' below at v2.12.0
6836  {
6837  StoppedForTrainInFront = false;
6838  TrainInFront = false;
6839  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6840  Utilities->CallLogPop(2456);
6841  return(false);
6842  }
6843  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6844  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6845  if((FrontPos > -1) && (TrainMode == Signaller) && TrainInFront) //check if train in front still there
6846  {
6847  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6848  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6849  {
6850  Able = true;
6851  TrainInFront = false;
6852  }
6853  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 == -1))
6854  {
6855  Able = true;
6856  TrainInFront = false;
6857  }
6858  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 == -1))
6859  {
6860  Able = true;
6861  TrainInFront = false;
6862  }
6863  else
6864  {
6865  Able = false; //added at v2.12.0 as train still in front so don't want signaller popup options to move available
6866  }
6867  }
6868  }
6869  else // leaving at a continuation so keep going
6870  {
6871  Able = true;
6872  StoppedForTrainInFront = false;
6873  TrainInFront = false;
6874  }
6875  Utilities->CallLogPop(1454);
6876  return(Able);
6877 }
6878 
6879 // ---------------------------------------------------------------------------
6880 
6882 {
6883  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
6884  // won't be set; if there is a train then set StoppedForTrainInFront
6885  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignalOrTrainInFront" + "," + HeadCode);
6886  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
6887  if(LeadElement == -1) // exiting at continuation
6888  {
6889  Utilities->CallLogPop(2045);
6890  return(false);
6891  }
6892  // end of addition
6893  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
6894  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
6895 
6896  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
6897  {
6898  TrainInFront = true;
6899  Utilities->CallLogPop(1455);
6900  return(false);
6901  }
6902  else
6903  {
6904  Utilities->CallLogPop(1456);
6906  // StoppedWithoutPower added v2.4.0
6907  }
6908 }
6909 
6910 // ---------------------------------------------------------------------------
6911 
6913 {
6914  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
6915  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
6916  TColor TempColour = BackgroundColour;
6917 
6918  UnplotTrain(8);
6921  StartSpeed = 0;
6922  PlotStartPosition(2);
6923  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
6924 
6925  //now erase a stub route if there is one, added at v2.5.1
6926  //first element of route is now immediately behind the train (i.e. next to MidElement)
6927  if(MidEntryPos >= 0)
6928  {
6929  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
6930  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6931  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6932  int RouteNumber = -1;
6933  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6934  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6935  {
6936  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
6937  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
6938  //elements can continue to be removed from that route
6939  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
6940 // if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6941  { //above condition removed at v2.17.0 so non-facing signal or continuation doesn't stop route being removed
6942  //if it is a facing signal then it will be detected below and not removed
6943  bool FirstPass = true; //added at v2.8.0
6944  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6945  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
6946  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
6947  int TVPos2 = PDE.GetTrackVectorPosition();
6948  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6949  {
6950  break;
6951  }
6952  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
6954  {
6955  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6956  }
6957  else
6958  {
6959  break;
6960  }
6961  FirstPass = false;
6962  }
6963  AllRoutes->RebuildRailwayFlag = true;
6964  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6965  }
6966  }
6967  }
6968  Utilities->CallLogPop(1102);
6969 }
6970 
6971 // ---------------------------------------------------------------------------
6972 
6974 {
6975  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6976  ",FloatingLabelNextString" + "," + HeadCode);
6977  AnsiString RetStr = "", LocationName = "";
6978  //record action time - may be arrival, departure or event for use later (added at v2.13.2)
6979  TDateTime ActionTime = Ptr->ArrivalTime;
6980  if(ActionTime == TDateTime(-1))
6981  {
6982  ActionTime = Ptr->DepartureTime;
6983  }
6984  if(ActionTime == TDateTime(-1))
6985  {
6986  ActionTime = Ptr->EventTime;
6987  }
6988  //If ActionTime still TDateTime(-1) then the train has terminated and 'None...' will be returned
6989  //Now correct it for repeats
6990  if(ActionTime != TDateTime(-1))
6991  {
6992  ActionTime = GetTrainTime(64, ActionTime);
6993  }
6994  if(int(DelayedRandMins) > 0)
6995  {
6996  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
6997  {
6998  throw Exception("Error - start entry in FloatingLabelNextString");
6999  }
7000  if(Ptr->FormatType == TimeTimeLoc)
7001  {
7002  if(TrainMode == Timetable)
7003  {
7004  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
7005  // not arrived yet in tt mode
7006  {
7007  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7008  }
7009  else
7010  {
7011  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(3, Ptr->DepartureTime));
7012  }
7013  }
7014  else // TrainMode == Signaller
7015  {
7016  if(!DepartureTimeSet) // not arrived yet
7017  {
7018  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7019  }
7020  else
7021  {
7022  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(36, Ptr->DepartureTime));
7023  }
7024  }
7025  }
7026  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7027  {
7028  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7029  }
7030  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7031  {
7032  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(5, Ptr->DepartureTime));
7033  }
7034  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7035  {
7036  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(46, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7037  }
7038  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7039  {
7040  RetStr = "Pass " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7041  }
7042  else if(Ptr->Command == "Fns")
7043  {
7044  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7045  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7046  RetStr = GetNewServiceDepartureInfo(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7047  }
7048  else if(Ptr->Command == "F-nshs")
7049  {
7050  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7051  Utilities->Format96HHMM(GetTrainTime(32, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7052  RetStr = GetNewServiceDepartureInfo(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7053  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7054  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7055  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7056  }
7057  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7058  {
7059  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7060  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7061  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7062  RetStr = GetNewServiceDepartureInfo(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7063  }
7064  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7065  {
7066  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7067  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7068  RetStr = GetNewServiceDepartureInfo(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7069  }
7070  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7071  {
7072  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7073  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7074  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7075  RetStr = GetNewServiceDepartureInfo(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7076  }
7077  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7078  {
7079  RetStr ="None, train terminated at " + Ptr->LocationName;
7080  }
7081  else if(Ptr->Command == "Frh")
7082  {
7083  RetStr = "None, train terminated at " + Ptr->LocationName;
7084  }
7085  else if(Ptr->Command == "Fer")
7086  {
7087  AnsiString AllowedExits = "";
7088  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at approx. " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime + TDateTime(DelayedRandMins/1440))) + AllowedExits;
7089  }
7090  else if(Ptr->Command == "Fjo")
7091  {
7092  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at approx. " +
7093  Utilities->Format96HHMM(GetTrainTime(11, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7094  }
7095  else if(Ptr->Command == "jbo")
7096  {
7097  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7098  " at approx. " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7099  }
7100  else if(Ptr->Command == "fsp")
7101  {
7102  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7103  " at approx. " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7104  }
7105  else if(Ptr->Command == "rsp")
7106  {
7107  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7108  " at approx. " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7109  }
7110  else if(Ptr->Command == "cdt")
7111  {
7112  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7113  }
7114  else if(Ptr->Command == "dsc")
7115  {
7116  RetStr = "Change description at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(65, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7117  }
7118  }
7119  else if(TrainController->TTClockTime > ActionTime) //condition added at v2.13.2 for trains that are delayed other than suffering a random delay
7120  {
7121  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7122  {
7123  throw Exception("Error - start entry in FloatingLabelNextString where TTClockTime > ActionTime");
7124  }
7125  if(Ptr->FormatType == TimeTimeLoc)
7126  {
7127  if(TrainMode == Timetable)
7128  {
7129  if(!TrainAtLocation(4, LocationName) || (LocationName != Ptr->LocationName))
7130  // not arrived yet in tt mode
7131  {
7132  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7133  }
7134  else
7135  {
7136  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7137  }
7138  }
7139  else // TrainMode == Signaller
7140  {
7141  if(!DepartureTimeSet) // not arrived yet
7142  {
7143  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7144  }
7145  else
7146  {
7147  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7148  }
7149  }
7150  }
7151  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7152  {
7153  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7154  }
7155  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7156  {
7157  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7158  }
7159  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7160  {
7161  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7162  }
7163  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7164  {
7165  RetStr = "Pass " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7166  }
7167  else if(Ptr->Command == "Fns")
7168  {
7169  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(60, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7170  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7171  RetStr = GetNewServiceDepartureInfo(19, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7172  }
7173  else if(Ptr->Command == "F-nshs")
7174  {
7175  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7177  RetStr = GetNewServiceDepartureInfo(20, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7178  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7179  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7180  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7181  }
7182  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7183  {
7184  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(61, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7185  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7186  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7187  RetStr = GetNewServiceDepartureInfo(21, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7188  }
7189  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7190  {
7191  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7192  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7193  RetStr = GetNewServiceDepartureInfo(22, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7194  }
7195  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7196  {
7197  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(62, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7198  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7199  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7200  RetStr = GetNewServiceDepartureInfo(23, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7201  }
7202  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7203  {
7204  RetStr ="None, train terminated at " + Ptr->LocationName;
7205  }
7206  else if(Ptr->Command == "Frh")
7207  {
7208  RetStr = "None, train terminated at " + Ptr->LocationName;
7209  }
7210  else if(Ptr->Command == "Fer")
7211  {
7212  AnsiString AllowedExits = "";
7213  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(5, Ptr->ExitList, AllowedExits) /*+ " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime)*/ + AllowedExits;
7214  }
7215  else if(Ptr->Command == "Fjo")
7216  {
7217  RetStr = "Join " + TrainController->GetRepeatHeadCode(63, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// + " at approx. " +
7218 // Utilities->Format96HHMM(TrainController->TTClockTime);
7219  }
7220  else if(Ptr->Command == "jbo")
7221  {
7222  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(64, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// +
7223 // " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7224  }
7225  else if(Ptr->Command == "fsp")
7226  {
7227  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(65, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7228  " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7229  }
7230  else if(Ptr->Command == "rsp")
7231  {
7232  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(66, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7233  " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7234  }
7235  else if(Ptr->Command == "cdt")
7236  {
7237  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7238  }
7239  else if(Ptr->Command == "dsc")
7240  {
7241  RetStr = "Change description at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7242  }
7243  }
7244  else //train not delayed
7245  {
7246  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7247  {
7248  throw Exception("Error - start entry in FloatingLabelNextString in final 'else'");
7249  }
7250  if(Ptr->FormatType == TimeTimeLoc)
7251  {
7252  if(TrainMode == Timetable)
7253  {
7254  if(!TrainAtLocation(3, LocationName) || (LocationName != Ptr->LocationName))
7255  // not arrived yet in tt mode
7256  {
7257  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(48, Ptr->ArrivalTime));
7258  }
7259  else
7260  {
7261  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(3, Ptr->DepartureTime));
7262  }
7263  }
7264  else // TrainMode == Signaller
7265  {
7266  if(!DepartureTimeSet) // not arrived yet
7267  {
7268  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(49, Ptr->ArrivalTime));
7269  }
7270  else
7271  {
7272  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(36, Ptr->DepartureTime));
7273  }
7274  }
7275  }
7276  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7277  {
7278  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(50, Ptr->ArrivalTime));
7279  }
7280  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7281  {
7282  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(5, Ptr->DepartureTime));
7283  }
7284  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7285  {
7286  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(51, Ptr->EventTime));
7287  }
7288  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7289  {
7290  RetStr = "Pass " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(52, Ptr->EventTime));
7291  }
7292  else if(Ptr->Command == "Fns")
7293  {
7294  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(53, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7295  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(53, Ptr->EventTime));
7296  RetStr = GetNewServiceDepartureInfo(10, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7297  }
7298  else if(Ptr->Command == "F-nshs")
7299  {
7300  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7302  RetStr = GetNewServiceDepartureInfo(12, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7303  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7304  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7305  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7306  }
7307  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7308  {
7309  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(54, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7310  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(55, Ptr->EventTime));
7311  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7312  RetStr = GetNewServiceDepartureInfo(14, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7313  }
7314  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7315  {
7316  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7317  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(56, Ptr->EventTime));
7318  RetStr = GetNewServiceDepartureInfo(16, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7319  }
7320  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7321  {
7322  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(55, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7323  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(57, Ptr->EventTime));
7324  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7325  RetStr = GetNewServiceDepartureInfo(18, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7326  }
7327  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7328  {
7329  RetStr ="None, train terminated at " + Ptr->LocationName;
7330  }
7331  else if(Ptr->Command == "Frh")
7332  {
7333  RetStr = "None, train terminated at " + Ptr->LocationName;
7334  }
7335  else if(Ptr->Command == "Fer")
7336  {
7337  AnsiString AllowedExits = "";
7338  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(4, Ptr->ExitList, AllowedExits) /*+ " at approx. " + Utilities->Format96HHMM(GetTrainTime(62, Ptr->EventTime))*/ + AllowedExits;
7339  }
7340  else if(Ptr->Command == "Fjo")
7341  {
7342  RetStr = "Join " + TrainController->GetRepeatHeadCode(56, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// + " at approx. " +
7343 // Utilities->Format96HHMM(GetTrainTime(58, Ptr->EventTime));
7344  }
7345  else if(Ptr->Command == "jbo")
7346  {
7347  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(57, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// +
7348 // " at approx. " + Utilities->Format96HHMM(GetTrainTime(59, Ptr->EventTime));
7349  }
7350  else if(Ptr->Command == "fsp")
7351  {
7352  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(58, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7353  " at approx. " + Utilities->Format96HHMM(GetTrainTime(60, Ptr->EventTime));
7354  }
7355  else if(Ptr->Command == "rsp")
7356  {
7357  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(59, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7358  " at approx. " + Utilities->Format96HHMM(GetTrainTime(61, Ptr->EventTime));
7359  }
7360  else if(Ptr->Command == "cdt")
7361  {
7362  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(63, Ptr->EventTime));
7363  }
7364  else if(Ptr->Command == "dsc")
7365  {
7366  RetStr = "Change description at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(66, Ptr->EventTime));
7367  }
7368  }
7369  Utilities->CallLogPop(1124);
7370  return(RetStr);
7371 }
7372 
7373 // ---------------------------------------------------------------------------
7374 /* as was
7375 AnsiString TTrain::FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
7376 {
7377  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7378  ",FloatingLabelNextString" + "," + HeadCode);
7379  AnsiString RetStr = "", LocationName = "";
7380 
7381  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7382  {
7383  throw Exception("Error - start entry in FloatingLabelNextString");
7384  }
7385  if(Ptr->FormatType == TimeTimeLoc)
7386  {
7387  if(TrainMode == Timetable)
7388  {
7389  if(!TrainAtLocation(, LocationName) || (LocationName != Ptr->LocationName))
7390  // not arrived yet in tt mode
7391  {
7392  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7393  }
7394  else
7395  {
7396  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7397  }
7398  }
7399  else // TrainMode == Signaller
7400  {
7401  if(!DepartureTimeSet) // not arrived yet
7402  {
7403  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7404  }
7405  else
7406  {
7407  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7408  }
7409  }
7410  }
7411  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7412  {
7413  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7414  }
7415  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7416  {
7417  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7418  }
7419  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7420  {
7421  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7422  }
7423  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7424  {
7425  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7426  }
7427  else if(Ptr->Command == "Fns")
7428  {
7429  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7430  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7431  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7432  }
7433  else if(Ptr->Command == "F-nshs")
7434  {
7435  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
7436  Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7437  RetStr = GetNewServiceDepartureInfo(, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7438  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7439  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7440  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7441  }
7442  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7443  {
7444  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7445  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7446  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7447  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7448  }
7449  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7450  {
7451  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7452  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7453  RetStr = GetNewServiceDepartureInfo(, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7454  }
7455  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7456  {
7457  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7458  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7459  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7460  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7461  }
7462  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7463  {
7464  RetStr ="None, train terminated at " + Ptr->LocationName;
7465  }
7466  else if(Ptr->Command == "Frh")
7467  {
7468  RetStr = "None, train terminated at " + Ptr->LocationName;
7469  }
7470  else if(Ptr->Command == "Fer")
7471  {
7472  AnsiString AllowedExits = "";
7473  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime)) + AllowedExits;
7474  }
7475  else if(Ptr->Command == "Fjo")
7476  {
7477  RetStr = "Join " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
7478  Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7479  }
7480  else if(Ptr->Command == "jbo")
7481  {
7482  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7483  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7484  }
7485  else if(Ptr->Command == "fsp")
7486  {
7487  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7488  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7489  }
7490  else if(Ptr->Command == "rsp")
7491  {
7492  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7493  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7494  }
7495  else if(Ptr->Command == "cdt")
7496  {
7497  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7498  }
7499  Utilities->CallLogPop();
7500  return(RetStr);
7501 }
7502 */
7503 // ---------------------------------------------------------------------------
7504 
7505 AnsiString TTrain::GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr, bool TimetableTime)
7506 { //last bool added at v2.13.2 so departure info adds random delay if actual rather than not timetable time required
7507  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
7508  + AnsiString(RptNum) + ",GetNewServiceDepartureInfo," + HeadCode);
7509  AnsiString DepTime = "", EventTime = "";
7510  bool CDTFlag = false; //reports if train changes direction before departs
7511  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
7512  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
7513  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
7514  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
7515  {
7516  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
7517  {
7518  TowardsLocation = AVI->LocationName;
7519  }
7520  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
7521  {
7522  TTrackElement TE = Track->TrackElementAt(1452, (AVI->ExitList.front()));
7523  if(TE.ActiveTrackElementName != "")
7524  {
7525  TowardsLocation = TE.ActiveTrackElementName;
7526  }
7527  else
7528  {
7529  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
7530  }
7531  }
7532  }
7533 
7534  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
7535  {
7536  if(AVI->Command == "cdt")
7537  {
7538  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
7539  continue;
7540  }
7541  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
7542  {
7543  TDateTime TTTime = TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes);
7544  if((DelayedRandMins >= 1) && !TimetableTime)
7545  {
7546  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7547  }
7548  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7549  {
7551  }
7552  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7553  {
7554  EventTime = Utilities->Format96HHMM(TTTime);
7555  }
7556  RetStr += "\nNew service splits at approx. " + EventTime;
7557  Utilities->CallLogPop(2234);
7558  return(RetStr);
7559  }
7560  if(AVI->Command == "jbo") //added at v2.15.0
7561  {
7562  TDateTime TTTime = TrainController->GetControllerTrainTime(28, AVI->EventTime, RptNum, IncrementalMinutes);
7563  if((DelayedRandMins >= 1) && !TimetableTime)
7564  {
7565  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7566  }
7567  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7568  {
7570  }
7571  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7572  {
7573  EventTime = Utilities->Format96HHMM(TTTime);
7574  }
7575  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at approx. " + EventTime;
7576  Utilities->CallLogPop(2595);
7577  return(RetStr);
7578  }
7579  if((AVI->Command == "Fns") || (AVI->Command == "F-nshs") || (AVI->Command == "Fns-sh")) //added at v2.15.0
7580  {
7581  TDateTime TTTime = TrainController->GetControllerTrainTime(29, AVI->EventTime, RptNum, IncrementalMinutes);
7582  if((DelayedRandMins >= 1) && !TimetableTime)
7583  {
7584  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7585  }
7586  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7587  {
7589  }
7590  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7591  {
7592  EventTime = Utilities->Format96HHMM(TTTime);
7593  }
7594  RetStr += "\nNew service finishes and forms another new service at approx. " + EventTime;
7595  Utilities->CallLogPop(2615);
7596  return(RetStr);
7597  }
7598  if(AVI->Command == "Fjo") //added at v2.15.0
7599  {
7600  TDateTime TTTime = TrainController->GetControllerTrainTime(26, AVI->EventTime, RptNum, IncrementalMinutes);
7601  if((DelayedRandMins >= 1) && !TimetableTime)
7602  {
7603  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7604  }
7605  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7606  {
7608  }
7609  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7610  {
7611  EventTime = Utilities->Format96HHMM(TTTime);
7612  }
7613  RetStr += "\nNew service finishes and joins " + AVI->OtherHeadCode + " at approx. " + EventTime;
7614  Utilities->CallLogPop(2605);
7615  return(RetStr);
7616  }
7617  if(AVI->Command == "Frh") //added at v2.15.0
7618  {
7619  RetStr += "\nNew service finishes and remains at the location.";
7620  Utilities->CallLogPop(2606);
7621  return(RetStr);
7622  }
7623  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
7624  {
7625  if(TimetableTime) //don't add random delay
7626  {
7627  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
7628  if(CDTFlag)
7629  {
7630  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7631  {
7632  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
7633  }
7634  else
7635  {
7636  RetStr += "\nNew service changes direction then departs at " + DepTime;
7637  }
7638  }
7639  else
7640  {
7641  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7642  {
7643  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
7644  }
7645  else
7646  {
7647  RetStr += "\nNew service departs at " + DepTime;
7648  }
7649  }
7650  }
7651  else if(DelayedRandMins >= 1)//add random delay
7652  {
7653  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(24, AVI->DepartureTime + TDateTime(DelayedRandMins/1440), RptNum, IncrementalMinutes));
7654  if(CDTFlag)
7655  {
7656  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7657  {
7658  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at approx. " + DepTime;
7659  }
7660  else
7661  {
7662  RetStr += "\nNew service changes direction then departs at approx. " + DepTime;
7663  }
7664  }
7665  else
7666  {
7667  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7668  {
7669  RetStr += "\nNew service departs towards " + TowardsLocation + " at approx. " + DepTime;
7670  }
7671  else
7672  {
7673  RetStr += "\nNew service departs at approx. " + DepTime;
7674  }
7675  }
7676  }
7677  else //no random delay but may be delayed for other reasons
7678  {
7679  TDateTime TTTime = TrainController->GetControllerTrainTime(25, AVI->DepartureTime, RptNum, IncrementalMinutes);
7680  if(TrainController->TTClockTime > TTTime)
7681  {
7683  }
7684  else
7685  {
7686  DepTime = Utilities->Format96HHMM(TTTime);
7687  }
7688  if(CDTFlag)
7689  {
7690  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7691  {
7692  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at approx. " + DepTime;
7693  }
7694  else
7695  {
7696  RetStr += "\nNew service changes direction then departs at approx. " + DepTime;
7697  }
7698  }
7699  else
7700  {
7701  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7702  {
7703  RetStr += "\nNew service departs towards " + TowardsLocation + " at approx. " + DepTime;
7704  }
7705  else
7706  {
7707  RetStr += "\nNew service departs at approx. " + DepTime;
7708  }
7709  }
7710  }
7711  Utilities->CallLogPop(2236);
7712  return(RetStr);
7713  }
7714  }
7715  Utilities->CallLogPop(2208);
7716  return(RetStr); //if reach here then RetStr doesn't change
7717 }
7718 
7719 // ---------------------------------------------------------------------------
7720 
7722 // Enter with Ptr pointing to first action to be listed (i.e. next action)
7723 // If there are actions to be skipped but a departure is awaited (SkippedDeparture = true) then after the departure Ptr moves forward by SkipPtrValue
7724 {
7725  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7726  ",FloatingTimetableString" + "," + HeadCode);
7727  AnsiString RetStr = "", PartStr = "";
7728  int Count = 0;
7729  bool SkipDep = false, SkipDepActedOn = false; //SkipDepActedOn ensures only one SkipDep acted on
7730  AnsiString LocName = Ptr->LocationName;
7731 
7732  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
7733  // can start in signaller control so exclude this
7734  {
7735  throw Exception("Error - start entry in FloatingTimetableString");
7736  }
7737  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTimeLoc check later
7738  bool FirstPass = true;
7739  Ptr--; // because incremented at start of loop
7740 
7741  // different first TimeTimeLoc display if in signaller control
7742  do
7743  {
7744  Ptr++;
7745  if((Ptr->FormatType == Repeat) || TimetableFinished)
7746  {
7747  break;
7748  }
7749  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
7750  {
7751  AnsiString TrainLoc = "";
7752  if(TrainMode == Timetable)
7753  {
7754  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7755  {
7756  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7757  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7758  {
7759  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7760  }
7761  }
7762  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7763  {
7764  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7765  }
7766  else
7767  {
7768  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7769  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7770  Count++; // because there are 2 entries
7771  }
7772  }
7773  else // TrainMode == Signaller
7774  {
7775  if(DepartureTimeSet)
7776  {
7777  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7778  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7779  {
7780  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7781  }
7782  }
7783  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7784  {
7785  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7786  }
7787  else
7788  {
7789  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7790  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7791  Count++; // because there are 2 entries
7792  }
7793  }
7794  }
7795  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
7796  {
7797  AnsiString TrainLoc = "";
7798  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7799  {
7800  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7801  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7802  {
7803  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7804  }
7805  }
7806  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7807  {
7808  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7809  }
7810  else
7811  {
7812  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7813  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7814  Count++; // because there are 2 entries
7815  }
7816  }
7817  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7818  {
7819  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
7820  }
7821  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7822  {
7823  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7824  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7825  {
7826  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7827  }
7828  }
7829  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewService)
7830  { //note that TreatPassAsTimeLocDeparture can't be set if have SkippedDeparture
7831  PartStr = Utilities->Format96HHMM(GetTrainTime(47, Ptr->EventTime)) + ": Depart from " + Ptr->LocationName;
7832  }
7833  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7834  {
7835  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
7836  }
7837  else if(Ptr->Command == "Fns")
7838  {
7839  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
7840  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7841  PartStr = GetNewServiceDepartureInfo(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to PartStr
7842  }
7843  else if(Ptr->Command == "F-nshs")
7844  {
7845  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
7846  Ptr->LocationName;
7847  PartStr = GetNewServiceDepartureInfo(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
7848  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7849  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7850  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7851  }
7852  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7853  {
7854  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
7855  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7856  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7857  PartStr = GetNewServiceDepartureInfo(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
7858  }
7859  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7860  {
7861  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7862  +" at " + Ptr->LocationName;
7863  PartStr = GetNewServiceDepartureInfo(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
7864  }
7865  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7866  {
7867  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
7868  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7869  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7870  PartStr = GetNewServiceDepartureInfo(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
7871  }
7872  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7873  {
7874  PartStr = "Terminate at " + Ptr->LocationName;
7875  }
7876  else if(Ptr->Command == "Frh")
7877  {
7878  PartStr = "Terminate at " + Ptr->LocationName;
7879  }
7880  else if(Ptr->Command == "Fer")
7881  {
7882  AnsiString AllowedExits = "";
7883  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
7884  }
7885  else if(Ptr->Command == "Fjo")
7886  {
7887  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
7888  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7889  }
7890  else if(Ptr->Command == "jbo")
7891  {
7892  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
7893  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7894  }
7895  else if(Ptr->Command == "fsp")
7896  {
7897  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
7898  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7899  if(Ptr->SplitDistribution != "")
7900  {
7901  PartStr+= ", split mass%-Power% = " + Ptr->SplitDistribution;
7902  }
7903  else
7904  {
7905  PartStr+= ", split mass%-Power% = 50-50";
7906  }
7907  }
7908  else if(Ptr->Command == "rsp")
7909  {
7910  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
7911  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7912  if(Ptr->SplitDistribution != "")
7913  {
7914  PartStr+= ", split mass%-Power% = " + Ptr->SplitDistribution;
7915  }
7916  else
7917  {
7918  PartStr+= ", split mass%-Power% = 50-50";
7919  }
7920  }
7921  else if(Ptr->Command == "cdt")
7922  {
7923  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
7924  }
7925  else if(Ptr->Command == "dsc")
7926  {
7927  PartStr = Utilities->Format96HHMM(GetTrainTime(67, Ptr->EventTime)) + ": Change description at " + Ptr->LocationName;
7928  }
7929  if(RetStr != "")
7930  {
7931  RetStr = RetStr + '\n' + PartStr;
7932  }
7933  else
7934  {
7935  RetStr = PartStr;
7936  }
7937  FirstPass = false;
7938  Count++;
7939 
7940  if(SkipDep)
7941  {
7942  Ptr = &(TrainDataEntryPtr->ActionVector.at(0)) + SkipPtrValue;
7943  Ptr--; //it is incremented at the start of the next loop
7944  SkipDep = false;
7945  SkipDepActedOn = true;
7946  }
7947  }
7948  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
7949  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
7950  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
7951  // forward as anyone should wish to see without looking at the full timetable
7952  if(TimetableFinished)
7953  {
7954  if(TrainMode == Timetable)
7955  {
7956  RetStr = "Timetable finished";
7957  }
7958  else
7959  {
7960  RetStr = "No timetable";
7961  }
7962  }
7963  Utilities->CallLogPop(1125);
7964  return("Timetable:\n" + RetStr);
7965 }
7966 
7967 // ---------------------------------------------------------------------------
7968 
7969 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
7970 {
7971  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
7972  Utilities->SaveFileString(OutFile, HeadCode);
7975  Utilities->SaveFileInt(OutFile, StartSpeed);
7978  Utilities->SaveFileInt(OutFile, RepeatNumber);
7981  Utilities->SaveFileInt(OutFile, Mass);
7984  Utilities->SaveFileDouble(OutFile, EntrySpeed);
7991  Utilities->SaveFileDouble(OutFile, BrakeRate);
7995  Utilities->SaveFileDouble(OutFile, double(EntryTime));
7996  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
7997  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
7998  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
7999  Utilities->SaveFileDouble(OutFile, double(TRSTime));
8000  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
8004  Utilities->SaveFileInt(OutFile, (short)TrainMode);
8009  Utilities->SaveFileBool(OutFile, Derailed);
8011  Utilities->SaveFileBool(OutFile, Crashed);
8018  Utilities->SaveFileBool(OutFile, NotInService);
8019  Utilities->SaveFileBool(OutFile, Plotted);
8020  Utilities->SaveFileBool(OutFile, TrainGone);
8021  Utilities->SaveFileBool(OutFile, SPADFlag);
8023  Utilities->SaveFileInt(OutFile, HOffset[0]);
8024  Utilities->SaveFileInt(OutFile, HOffset[1]);
8025  Utilities->SaveFileInt(OutFile, HOffset[2]);
8026  Utilities->SaveFileInt(OutFile, HOffset[3]);
8027  Utilities->SaveFileInt(OutFile, VOffset[0]);
8028  Utilities->SaveFileInt(OutFile, VOffset[1]);
8029  Utilities->SaveFileInt(OutFile, VOffset[2]);
8030  Utilities->SaveFileInt(OutFile, VOffset[3]);
8031  Utilities->SaveFileInt(OutFile, PlotElement[0]);
8032  Utilities->SaveFileInt(OutFile, PlotElement[1]);
8033  Utilities->SaveFileInt(OutFile, PlotElement[2]);
8034  Utilities->SaveFileInt(OutFile, PlotElement[3]);
8035  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
8036  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
8037  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
8038  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
8040  Utilities->SaveFileInt(OutFile, (short)Straddle);
8041  Utilities->SaveFileInt(OutFile, NextTrainID);
8042  Utilities->SaveFileInt(OutFile, TrainID);
8043  Utilities->SaveFileInt(OutFile, LeadElement);
8044  Utilities->SaveFileInt(OutFile, LeadEntryPos);
8045  Utilities->SaveFileInt(OutFile, LeadExitPos);
8046  Utilities->SaveFileInt(OutFile, MidElement);
8047  Utilities->SaveFileInt(OutFile, MidEntryPos);
8048  Utilities->SaveFileInt(OutFile, MidExitPos);
8049  Utilities->SaveFileInt(OutFile, LagElement);
8050  Utilities->SaveFileInt(OutFile, LagEntryPos);
8051  Utilities->SaveFileInt(OutFile, LagExitPos);
8052  int ColourNumber;
8053 
8055  {
8056  ColourNumber = 0;
8057  }
8059  {
8060  ColourNumber = 1;
8061  }
8063  {
8064  ColourNumber = 2;
8065  }
8067  {
8068  ColourNumber = 3;
8069  }
8071  {
8072  ColourNumber = 4;
8073  }
8075  {
8076  ColourNumber = 5;
8077  }
8079  {
8080  ColourNumber = 6;
8081  }
8083  {
8084  ColourNumber = 7;
8085  }
8087  {
8088  ColourNumber = 8;
8089  }
8091  {
8092  ColourNumber = 9;
8093  }
8095  {
8096  ColourNumber = 10;
8097  }
8099  {
8100  ColourNumber = 11;
8101  }
8103  {
8104  ColourNumber = 12;
8105  }
8106  else if(BackgroundColour == clTRSBackground)
8107  {
8108  ColourNumber = 13;
8109  }
8111  {
8112  ColourNumber = 14; // added at v2.4.0
8113  }
8114  Utilities->SaveFileInt(OutFile, ColourNumber);
8115 
8116  // additional data
8117  bool ForwardHeadCode;
8118 
8119  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
8120  {
8121  ForwardHeadCode = true;
8122  }
8123  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
8124  else
8125  {
8126  ForwardHeadCode = false;
8127  }
8128  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
8129 
8130  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
8131 
8132  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
8133  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
8134 
8135  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
8136  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
8137  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
8138  // so use the last asterisk position for this - 0 for false & 1 for true
8139  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
8140  AnsiString Marker;
8141 
8143  {
8144  Marker = "*****1";
8145  }
8146  else
8147  {
8148  Marker = "*****0";
8149  }
8150  if(RestoreTimetableLocation == "")
8151  {
8152  Utilities->SaveFileString(OutFile, Marker);
8153  }
8154  else
8155  {
8156  AnsiString CombinedString = RestoreTimetableLocation + Marker;
8157  Utilities->SaveFileString(OutFile, CombinedString);
8158  // RestoreTimetableLocation + marker
8159  }
8160  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
8161  Utilities->CallLogPop(1457);
8162 }
8163 
8164 // ---------------------------------------------------------------------------
8165 
8166 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
8167 {
8168  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
8169  HeadCode = Utilities->LoadFileString(InFile);
8172  StartSpeed = Utilities->LoadFileInt(InFile);
8174  if(SignallerMaxSpeed < 10)
8175  {
8176  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
8177  }
8179  RepeatNumber = Utilities->LoadFileInt(InFile);
8182  Mass = Utilities->LoadFileInt(InFile);
8185  {
8187  }
8188  // above added at v2.1.0 for legacy session files where value may not have been limited
8190  EntrySpeed = Utilities->LoadFileDouble(InFile);
8194  if(TimetableMaxRunningSpeed < 10)
8195  {
8196  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8197  }
8199  if(MaxRunningSpeed < 10)
8200  {
8201  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8202  }
8205  BrakeRate = Utilities->LoadFileDouble(InFile);
8209  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
8210  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
8211  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
8212  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
8213  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
8214  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
8223  Derailed = Utilities->LoadFileBool(InFile);
8225  Crashed = Utilities->LoadFileBool(InFile);
8232  NotInService = Utilities->LoadFileBool(InFile);
8233  Plotted = Utilities->LoadFileBool(InFile);
8234  TrainGone = Utilities->LoadFileBool(InFile);
8235  SPADFlag = Utilities->LoadFileBool(InFile);
8237  HOffset[0] = Utilities->LoadFileInt(InFile);
8238  HOffset[1] = Utilities->LoadFileInt(InFile);
8239  HOffset[2] = Utilities->LoadFileInt(InFile);
8240  HOffset[3] = Utilities->LoadFileInt(InFile);
8241  VOffset[0] = Utilities->LoadFileInt(InFile);
8242  VOffset[1] = Utilities->LoadFileInt(InFile);
8243  VOffset[2] = Utilities->LoadFileInt(InFile);
8244  VOffset[3] = Utilities->LoadFileInt(InFile);
8245  PlotElement[0] = Utilities->LoadFileInt(InFile);
8246  PlotElement[1] = Utilities->LoadFileInt(InFile);
8247  PlotElement[2] = Utilities->LoadFileInt(InFile);
8248  PlotElement[3] = Utilities->LoadFileInt(InFile);
8249  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
8250  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
8251  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
8252  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
8254  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
8255  NextTrainID = Utilities->LoadFileInt(InFile);
8256  // will be same for all but best to save all anyway
8257  TrainID = Utilities->LoadFileInt(InFile);
8258  LeadElement = Utilities->LoadFileInt(InFile);
8259  LeadEntryPos = Utilities->LoadFileInt(InFile);
8260  LeadExitPos = Utilities->LoadFileInt(InFile);
8261  MidElement = Utilities->LoadFileInt(InFile);
8262  MidEntryPos = Utilities->LoadFileInt(InFile);
8263  MidExitPos = Utilities->LoadFileInt(InFile);
8264  LagElement = Utilities->LoadFileInt(InFile);
8265  LagEntryPos = Utilities->LoadFileInt(InFile);
8266  LagExitPos = Utilities->LoadFileInt(InFile);
8267  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
8268 
8269  if(ColourNumber == 0)
8270  {
8272  }
8273  else if(ColourNumber == 1)
8274  {
8276  }
8277  else if(ColourNumber == 2)
8278  {
8280  }
8281  else if(ColourNumber == 3)
8282  {
8284  }
8285  else if(ColourNumber == 4)
8286  {
8288  }
8289  else if(ColourNumber == 5)
8290  {
8292  }
8293  else if(ColourNumber == 6)
8294  {
8296  }
8297  else if(ColourNumber == 7)
8298  {
8300  }
8301  else if(ColourNumber == 8)
8302  {
8304  }
8305  else if(ColourNumber == 9)
8306  {
8308  }
8309  else if(ColourNumber == 10)
8310  {
8312  }
8313  else if(ColourNumber == 11)
8314  {
8316  }
8317  else if(ColourNumber == 12)
8318  {
8320  }
8321  else if(ColourNumber == 13)
8322  {
8324  }
8325  else if(ColourNumber == 14)
8326  {
8327  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
8328 
8329  }
8330  // additional data
8332  // sets the BackgroundColour to the loaded value
8333  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
8334 
8335  if(ForwardHeadCode)
8336  {
8337  for(int x = 0; x < 4; x++)
8338  {
8340  }
8341  }
8342  else
8343  {
8344  for(int x = 0; x < 4; x++)
8345  {
8346  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
8347  }
8348  }
8349  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
8350  if(TrainMode == Timetable)
8351  {
8352  if(Crashed)
8353  {
8355  }
8356  else
8357  {
8359  }
8360  }
8361  else
8362  {
8364  }
8366  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
8367  if(Straddle == LeadMid)
8368  {
8369  if(LeadElement > -1)
8370  {
8372  }
8373  if(LeadElement > -1)
8374  {
8376  }
8377  if(MidElement > -1)
8378  {
8380  }
8381  if(MidElement > -1)
8382  {
8384  }
8385  }
8386  else if(Straddle == LeadMidLag)
8387  {
8388  if(LeadElement > -1)
8389  {
8391  }
8392  if(MidElement > -1)
8393  {
8395  }
8396  if(MidElement > -1)
8397  {
8399  }
8400  if(LagElement > -1)
8401  {
8403  }
8404  }
8405  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
8406 
8407  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
8408  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
8409 
8410  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
8411 
8412  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
8413  if(LeadElement > -1)
8414  // need to include this in case train exiting & no lead element
8415  {
8417  {
8418  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
8419  }
8420  }
8421  AValue = sqrt(2 * PowerAtRail / Mass);
8422 
8423  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
8424 
8425  // possible RestoreTimetableLocation + Marker, where Marker is
8426  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
8427  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
8428  // added at beta v0.2e
8429  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
8430  // name not allowed to include the '*' character
8431  {
8432  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
8433  bool GiveMessagesFalse = false;
8434  bool CheckLocationsExistInRailwayTrue = true;
8435  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
8436  {
8437  // otherwise take no action
8438  RestoreTimetableLocation = Location;
8439  }
8440  }
8441  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
8442 
8443  StoppedWithoutPower = false;
8444  if(Marker[6] == '1')
8445  {
8446  StoppedWithoutPower = true;
8447  }
8448  Utilities->CallLogPop(1458);
8449 }
8450 
8451 // ---------------------------------------------------------------------------
8452 
8453 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
8454 {
8456  {
8457  return(false); // HeadCode
8458 
8459  }
8460  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8461  {
8462  return(false); // RearStartElement
8463 
8464  }
8465  if(!Utilities->CheckFileInt(InFile, 0, 3))
8466  {
8467  return(false); // RearStartExitPos
8468 
8469  }
8470  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8471  {
8472  return(false); // StartSpeed
8473 
8474  }
8475  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8476  {
8477  return(false); // SignallerMaxSpeed
8478 
8479  }
8480  if(!Utilities->CheckFileBool(InFile))
8481  {
8482  return(false); // HoldAtLocationInTTMode
8483 
8484  }
8485  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8486  {
8487  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
8488 
8489  }
8490  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8491  {
8492  return(false); // IncrementalMinutes (max 96 x 60)
8493 
8494  }
8495  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8496  {
8497  return(false); // IncrementalDigits
8498 
8499  }
8500  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8501  {
8502  return(false); // Mass
8503 
8504  }
8505  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
8506  {
8507  return(false);
8508  }
8509  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
8510  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
8511  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8512  {
8513  return(false); // FrontElementLength
8514 
8515  }
8516  if(!Utilities->CheckFileDouble(InFile))
8517  {
8518  return(false); // EntrySpeed
8519 
8520  }
8521  if(!Utilities->CheckFileDouble(InFile))
8522  {
8523  return(false); // ExitSpeedHalf
8524 
8525  }
8526  if(!Utilities->CheckFileDouble(InFile))
8527  {
8528  return(false); // ExitSpeedFull
8529 
8530  }
8531  if(!Utilities->CheckFileDouble(InFile))
8532  {
8533  return(false); // TimetableMaxRunningSpeed
8534 
8535  }
8536  if(!Utilities->CheckFileDouble(InFile))
8537  {
8538  return(false); // MaxRunningSpeed
8539 
8540  }
8541  if(!Utilities->CheckFileDouble(InFile))
8542  {
8543  return(false); // MaxExitSpeed
8544 
8545  }
8546  if(!Utilities->CheckFileDouble(InFile))
8547  {
8548  return(false); // MaxBrakeRate
8549 
8550  }
8551  if(!Utilities->CheckFileDouble(InFile))
8552  {
8553  return(false); // BrakeRate
8554 
8555  }
8556  if(!Utilities->CheckFileDouble(InFile))
8557  {
8558  return(false); // PowerAtRail
8559 
8560  }
8561  if(!Utilities->CheckFileBool(InFile))
8562  {
8563  return(false); // FirstHalfMove
8564 
8565  }
8566  if(!Utilities->CheckFileBool(InFile))
8567  {
8568  return(false); // OneLengthAccelDecel
8569 
8570  }
8571  if(!Utilities->CheckFileDouble(InFile))
8572  {
8573  return(false); // double(EntryTime)
8574 
8575  }
8576  if(!Utilities->CheckFileDouble(InFile))
8577  {
8578  return(false); // double(ExitTimeHalf)
8579 
8580  }
8581  if(!Utilities->CheckFileDouble(InFile))
8582  {
8583  return(false); // double(ExitTimeFull)
8584 
8585  }
8586  if(!Utilities->CheckFileDouble(InFile))
8587  {
8588  return(false); // double(ReleaseTime)
8589 
8590  }
8591  if(!Utilities->CheckFileDouble(InFile))
8592  {
8593  return(false); // double(TRSTime)
8594 
8595  }
8596  if(!Utilities->CheckFileDouble(InFile))
8597  {
8598  return(false); // double(LastActionTime)
8599 
8600  }
8601  if(!Utilities->CheckFileBool(InFile))
8602  {
8603  return(false); // CallingOnFlag
8604 
8605  }
8606  if(!Utilities->CheckFileBool(InFile))
8607  {
8608  return(false); // BeingCalledOn
8609 
8610  }
8611  if(!Utilities->CheckFileBool(InFile))
8612  {
8613  return(false); // DepartureTimeSet
8614 
8615  }
8616  if(!Utilities->CheckFileInt(InFile, 0, 2))
8617  {
8618  return(false); // (short)TrainMode
8619 
8620  }
8621  if(!Utilities->CheckFileBool(InFile))
8622  {
8623  return(false); // TimetableFinished
8624 
8625  }
8626  if(!Utilities->CheckFileBool(InFile))
8627  {
8628  return(false); // LastActionDelayFlag
8629 
8630  }
8631  if(!Utilities->CheckFileBool(InFile))
8632  {
8633  return(false); // SignallerRemoved
8634 
8635  }
8636  if(!Utilities->CheckFileBool(InFile))
8637  {
8638  return(false); // TerminatedMessageSent
8639 
8640  }
8641  if(!Utilities->CheckFileBool(InFile))
8642  {
8643  return(false); // Derailed
8644 
8645  }
8646  if(!Utilities->CheckFileBool(InFile))
8647  {
8648  return(false); // DerailPending
8649 
8650  }
8651  if(!Utilities->CheckFileBool(InFile))
8652  {
8653  return(false); // Crashed
8654 
8655  }
8656  if(!Utilities->CheckFileBool(InFile))
8657  {
8658  return(false); // StoppedAtBuffers
8659 
8660  }
8661  if(!Utilities->CheckFileBool(InFile))
8662  {
8663  return(false); // StoppedAtSignal
8664 
8665  }
8666  if(!Utilities->CheckFileBool(InFile))
8667  {
8668  return(false); // StoppedAtLocation
8669 
8670  }
8671  if(!Utilities->CheckFileBool(InFile))
8672  {
8673  return(false); // SignallerStopped
8674 
8675  }
8676  if(!Utilities->CheckFileBool(InFile))
8677  {
8678  return(false); // StoppedAfterSPAD
8679 
8680  }
8681  if(!Utilities->CheckFileBool(InFile))
8682  {
8683  return(false); // StoppedForTrainInFront
8684 
8685  }
8686  if(!Utilities->CheckFileBool(InFile))
8687  {
8688  return(false); // NotInService
8689 
8690  }
8691  if(!Utilities->CheckFileBool(InFile))
8692  {
8693  return(false); // Plotted
8694 
8695  }
8696  if(!Utilities->CheckFileBool(InFile))
8697  {
8698  return(false); // TrainGone
8699 
8700  }
8701  if(!Utilities->CheckFileBool(InFile))
8702  {
8703  return(false); // SPADFlag
8704 
8705  }
8706  if(!Utilities->CheckFileBool(InFile))
8707  {
8708  return(false); // TimeTimeLocArrived
8709 
8710  }
8711  if(!Utilities->CheckFileInt(InFile, 0, 15))
8712  {
8713  return(false); // HOffset[0]
8714 
8715  }
8716  if(!Utilities->CheckFileInt(InFile, 0, 15))
8717  {
8718  return(false); // HOffset[1]
8719 
8720  }
8721  if(!Utilities->CheckFileInt(InFile, 0, 15))
8722  {
8723  return(false); // HOffset[2]
8724 
8725  }
8726  if(!Utilities->CheckFileInt(InFile, 0, 15))
8727  {
8728  return(false); // HOffset[3]
8729 
8730  }
8731  if(!Utilities->CheckFileInt(InFile, 0, 15))
8732  {
8733  return(false); // VOffset[0]
8734 
8735  }
8736  if(!Utilities->CheckFileInt(InFile, 0, 15))
8737  {
8738  return(false); // VOffset[1]
8739 
8740  }
8741  if(!Utilities->CheckFileInt(InFile, 0, 15))
8742  {
8743  return(false); // VOffset[2]
8744 
8745  }
8746  if(!Utilities->CheckFileInt(InFile, 0, 15))
8747  {
8748  return(false); // VOffset[3]
8749 
8750  }
8751  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8752  {
8753  return(false); // PlotElement[0]
8754 
8755  }
8756  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8757  {
8758  return(false); // PlotElement[1]
8759 
8760  }
8761  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8762  {
8763  return(false); // PlotElement[2]
8764 
8765  }
8766  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8767  {
8768  return(false); // PlotElement[3]
8769 
8770  }
8771  if(!Utilities->CheckFileInt(InFile, 0, 3))
8772  {
8773  return(false); // PlotEntryPos[0]
8774 
8775  }
8776  if(!Utilities->CheckFileInt(InFile, 0, 3))
8777  {
8778  return(false); // PlotEntryPos[1]
8779 
8780  }
8781  if(!Utilities->CheckFileInt(InFile, 0, 3))
8782  {
8783  return(false); // PlotEntryPos[2]
8784 
8785  }
8786  if(!Utilities->CheckFileInt(InFile, 0, 3))
8787  {
8788  return(false); // PlotEntryPos[3]
8789 
8790  }
8791  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8792  {
8793  return(false); // TrainCrashedInto
8794 
8795  }
8796  if(!Utilities->CheckFileInt(InFile, 0, 2))
8797  {
8798  return(false); // (short)Straddle
8799 
8800  }
8801  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8802  {
8803  return(false); // NextTrainID
8804 
8805  }
8806  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8807  {
8808  return(false); // TrainID
8809 
8810  }
8811  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8812  {
8813  return(false); // LeadElement
8814 
8815  }
8816  if(!Utilities->CheckFileInt(InFile, 0, 3))
8817  {
8818  return(false); // LeadEntryPos
8819 
8820  }
8821  if(!Utilities->CheckFileInt(InFile, 0, 3))
8822  {
8823  return(false); // LeadExitPos
8824 
8825  }
8826  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8827  {
8828  return(false); // MidElement
8829 
8830  }
8831  if(!Utilities->CheckFileInt(InFile, 0, 3))
8832  {
8833  return(false); // MidEntryPos
8834 
8835  }
8836  if(!Utilities->CheckFileInt(InFile, 0, 3))
8837  {
8838  return(false); // MidExitPos
8839 
8840  }
8841  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8842  {
8843  return(false); // LagElement
8844 
8845  }
8846  if(!Utilities->CheckFileInt(InFile, 0, 3))
8847  {
8848  return(false); // LagEntryPos
8849 
8850  }
8851  if(!Utilities->CheckFileInt(InFile, 0, 3))
8852  {
8853  return(false); // LagExitPos
8854 
8855  }
8856  if(!Utilities->CheckFileInt(InFile, 0, 14))
8857  {
8858  return(false);
8859  }
8860  // Background colour number //14 is failed colour at v2.4.0
8861  if(!Utilities->CheckFileBool(InFile))
8862  {
8863  return(false); // ForwardHeadCode
8864 
8865  }
8866  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8867  {
8868  return(false); // TrainDataEntryValue
8869 
8870  }
8871  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8872  {
8873  return(false); // ActionVectorEntryValue
8874 
8875  }
8877  {
8878  return(false); // End of train marker + possible RestoreTimetableLocation
8879 
8880  }
8881  // and StoppedWithoutPower flag
8882  return(true);
8883 }
8884 
8885 // ---------------------------------------------------------------------------
8886 
8887 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
8888 {
8889  // order below reflects significance so earlier shows first, as may have more than one flag set
8890  // only plot flashing trains when Flash is true
8891 
8892 /*
8893  clCrashedBackground (TColor)0x0000FF red
8894  clDerailedBackground (TColor)0x0000FF red
8895  clSPADBackground (TColor)0x00FFFF yellow
8896  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
8897  clCallOnBackground (TColor)0xFF33FF light magenta
8898  clSignalStopBackground (TColor)0x00FF66 green
8899  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
8900  clStationStopBackground (TColor)0xCCFFCC pale green
8901  clTRSBackground (TColor)0xFFCCFF light pink
8902  clBufferStopBackground (TColor)0xFFFFCC pale cyan
8903  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
8904  clSignallerStopped (TColor)0x99CCFF caramel
8905  clNormalBackground (TColor)0xCCCCCC grey
8906 */
8907 
8908  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
8909  bool HideFlashingTrain = true;
8910  // hide it when Flash false so it blinks on and off
8911  // if don't hide it it stays displayed all the time
8912  Graphics::TBitmap *SmallTrainBitmap;
8913 
8914  // NB ensure retain same order as zoomed in order so colours correspond
8916  {
8917  TrainController->CrashWarning = true;
8918  SmallTrainBitmap = RailGraphics->smRed;
8919  }
8921  {
8923  SmallTrainBitmap = RailGraphics->smRed;
8924  }
8926  {
8927  TrainController->SPADWarning = true;
8928  SmallTrainBitmap = RailGraphics->smYellow;
8929  }
8931  {
8933  SmallTrainBitmap = RailGraphics->smOrange;
8934  }
8936  {
8938  SmallTrainBitmap = RailGraphics->smMagenta;
8939  }
8941  {
8943  SmallTrainBitmap = RailGraphics->smBrightGreen;
8944  }
8946  {
8948  SmallTrainBitmap = RailGraphics->smCyan;
8949  }
8951  {
8952  SmallTrainBitmap = RailGraphics->smPaleGreen;
8953  HideFlashingTrain = false;
8954  }
8956  {
8957  SmallTrainBitmap = RailGraphics->smCyan;
8958  HideFlashingTrain = false;
8959  }
8961  {
8962  SmallTrainBitmap = RailGraphics->smLightBlue;
8963  HideFlashingTrain = false;
8964  }
8966  {
8967  SmallTrainBitmap = RailGraphics->smCaramel;
8968  HideFlashingTrain = false;
8969  }
8970  else
8971  {
8972  SmallTrainBitmap = RailGraphics->smBlack; // moving
8973  HideFlashingTrain = false;
8974  }
8975  // now plot the new train
8976  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
8977  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
8978  {
8979  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
8980  }
8981  if((MidElement > -1) && (!HideFlashingTrain || Flash))
8982  {
8983  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
8984  }
8985  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
8986  {
8987  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
8988  }
8992  Utilities->CallLogPop(1459);
8993 }
8994 
8995 // ---------------------------------------------------------------------------
8996 
8998 {
8999  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
9000  if(!Display->ZoomOutFlag)
9001  {
9002  Utilities->CallLogPop(1304);
9003  return;
9004  }
9005  for(int y = 0; y < 3; y++)
9006  {
9007  if(OldZoomOutElement[y] > -1)
9008  {
9009  bool FoundFlag = false;
9010  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
9011  TTrackElement IATElement1, IATElement2;
9012  // default elements to begin with
9013  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
9014  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
9015  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
9016  if(FoundFlag)
9017  {
9018  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
9019  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
9020  if(IMPair.first != IMPair.second)
9021  {
9022  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
9023  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
9024  }
9025  }
9026  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
9027  }
9028  }
9029  Utilities->CallLogPop(1305);
9030 }
9031 
9032 // ---------------------------------------------------------------------------
9033 
9034 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
9035 {
9036  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
9037  LocationName = "";
9038  if(!RevisedStoppedAtLoc())
9039  {
9040  Utilities->CallLogPop(1398);
9041  return(false);
9042  }
9043  if(LeadElement > -1)
9044  {
9046  }
9047  if((LocationName == "") && (MidElement > -1))
9048  {
9049  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
9050  }
9051  if((LocationName == "") && (LagElement > -1))
9052  {
9053  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
9054  }
9055  if(LocationName == "")
9056  {
9057  throw Exception("Error - Location name not set in TrainAtLocation");
9058  }
9059  Utilities->CallLogPop(1399);
9060  return(true);
9061 }
9062 
9063 // ---------------------------------------------------------------------------
9064 
9065 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
9066 {
9067  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
9068  for(int x = 0; x < 4; x++)
9069  {
9070  PlotTrainGraphic(7, x, Disp);
9071  }
9072  Utilities->CallLogPop(647);
9073 }
9074 
9075 // ---------------------------------------------------------------------------
9076 
9077 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
9078 {
9079  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
9080  for(int x = 0; x < 4; x++)
9081  {
9082  if(PlotElement[x] > -1)
9083  {
9084  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
9085  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
9086  }
9087  }
9088  Utilities->CallLogPop(1708);
9089 }
9090 
9091 // ---------------------------------------------------------------------------
9092 
9093 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
9094 {
9095  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
9096  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
9097  AnsiString(LinkNumber) + "," + HeadCode);
9098 
9099 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
9100  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
9101  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
9102 */
9103 
9104  // note that MidElement always fully occupied
9105  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
9106  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
9107  {
9108  Utilities->CallLogPop(2005);
9109  return(true);
9110  }
9111  if(Straddle == LeadMid)
9112  {
9113  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
9114  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
9115  {
9116  Utilities->CallLogPop(2006);
9117  return(true);
9118  }
9119  }
9120  else if(Straddle == LeadMidLag)
9121  {
9122  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
9123  // only interested in LeadEntryPos as train not occupying ExitPos yet
9124  {
9125  Utilities->CallLogPop(2007);
9126  return(true);
9127  }
9128  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
9129  // only interested in LagExitPos as train has left EntryPos
9130  {
9131  Utilities->CallLogPop(2008);
9132  return(true);
9133  }
9134  }
9135  Utilities->CallLogPop(2009);
9136  return(false);
9137 }
9138 
9139 // ---------------------------------------------------------------------------
9140 
9141 float TTrain::CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair) // only called for running trains.
9146 // TimeToExit & ExitPair added for multiplayer
9147 {
9148  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
9149  int DistanceToRedSignal = 0, DistanceToExit = -1;
9150  float TimeToAct = 0, LastTimeToExit = TimeToExit;
9151  TimeToExit = -1;
9152  ExitPair.first = -1;
9153  ExitPair.second = -1;
9154  float MinsEarly = 0; //added at v2.6.1
9155  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
9156  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
9157  float TempTTE;
9158 
9159  if(TrainFailed && Stopped() && (TrainMode != Signaller))
9160  {
9161  Utilities->CallLogPop(2147);
9162  return(0); // time to act now, time to exit set above
9163  }
9164  if(SignallerStopped)
9165  {
9166  Utilities->CallLogPop(2080);
9167  return(-1); //time to exit set above
9168  }
9169  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
9170  {
9171  Utilities->CallLogPop(2266);
9172  return(-1); // time to exit set above
9173  }
9174 
9175  // check if exiting at a continuation, if so there's no action time needed but still need exit time & ExitPair
9176  if((LeadElement == -1) && (MidElement == -1) && (LagElement > -1) && (Track->TrackElementAt(1411, LagElement).TrackType == Continuation)
9177  /*&& (ExitSpeedFull > 1)*/) //LagElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9178  {
9179  if(Straddle == LeadMidLag) //only half of rear train element on exit, 0.5 lengths to exit
9180  {
9181  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9182  {
9183  TempTTE = (0.5 * Track->TrackElementAt(1412, LagElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9184  if(TempTTE < LastTimeToExit)
9185  {
9186  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9187  }
9188  else
9189  {
9190  TimeToExit = LastTimeToExit;
9191  }
9192  }
9193  else
9194  {
9195  TimeToExit = LastTimeToExit;
9196  }
9197  ExitPair.first = Track->TrackElementAt(1413, LagElement).HLoc;
9198  ExitPair.second = Track->TrackElementAt(1414, LagElement).VLoc;
9199  Utilities->CallLogPop(2342);
9200  return(-1);
9201  }
9202  else
9203  {
9204  TimeToExit = 0; //all train exited
9205  ExitPair.first = Track->TrackElementAt(1415, LagElement).HLoc;
9206  ExitPair.second = Track->TrackElementAt(1416, LagElement).VLoc;
9207  Utilities->CallLogPop(2343);
9208  return(-1);
9209  }
9210  }
9211  if((LeadElement == -1) && (MidElement > -1) && (Track->TrackElementAt(1417, MidElement).TrackType == Continuation)/* && (ExitSpeedFull > 1)*/)
9212  //here MidElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9213  {
9214  if(Straddle == LeadMidLag) //front element of train half off the exit, 1.5 lengths to exit
9215  {
9216  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9217  {
9218  TempTTE = (1.5 * Track->TrackElementAt(1418, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9219  if(TempTTE < LastTimeToExit)
9220  {
9221  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9222  }
9223  else
9224  {
9225  TimeToExit = LastTimeToExit;
9226  }
9227  }
9228  else
9229  {
9230  TimeToExit = LastTimeToExit;
9231  }
9232  ExitPair.first = Track->TrackElementAt(1419, MidElement).HLoc;
9233  ExitPair.second = Track->TrackElementAt(1420, MidElement).VLoc;
9234  Utilities->CallLogPop(2344);
9235  return(-1);
9236  }
9237  else //front element of train fully off the exit, one length to exit
9238  {
9239  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9240  {
9241  TempTTE = (Track->TrackElementAt(1421, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9242  if(TempTTE < LastTimeToExit)
9243  {
9244  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9245  }
9246  else
9247  {
9248  TimeToExit = LastTimeToExit;
9249  }
9250  }
9251  else
9252  {
9253  TimeToExit = LastTimeToExit;
9254  }
9255  ExitPair.first = Track->TrackElementAt(1422, MidElement).HLoc;
9256  ExitPair.second = Track->TrackElementAt(1423, MidElement).VLoc;
9257  Utilities->CallLogPop(2345);
9258  return(-1);
9259  }
9260  }
9261  if(LeadElement > -1)
9262  {
9264  /* && (ExitSpeedFull > 1)*/) //LeadElement is the exit. If LeadMidLag have lead half on exit or if LeadMid have lead full on exit
9265  { //if LeadMidLag have 2.5 lengths to exit, else have 2 lengths to exit
9266  //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9267  if(Straddle == LeadMidLag)
9268  {
9269  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9270  {
9271  TempTTE = (2.5 * Track->TrackElementAt(1426, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9272  if(TempTTE < LastTimeToExit)
9273  {
9274  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9275  }
9276  else
9277  {
9278  TimeToExit = LastTimeToExit;
9279  }
9280  }
9281  else
9282  {
9283  TimeToExit = LastTimeToExit;
9284  }
9285  ExitPair.first = Track->TrackElementAt(1427, LeadElement).HLoc;
9286  ExitPair.second = Track->TrackElementAt(1428, LeadElement).VLoc;
9287  Utilities->CallLogPop(2346);
9288  return(-1);
9289  }
9290  else
9291  {
9292  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9293  {
9294  TempTTE = (2 * Track->TrackElementAt(1429, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9295  if(TempTTE < LastTimeToExit)
9296  {
9297  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9298  }
9299  else
9300  {
9301  TimeToExit = LastTimeToExit;
9302  }
9303  }
9304  else
9305  {
9306  TimeToExit = LastTimeToExit;
9307  }
9308  ExitPair.first = Track->TrackElementAt(1430, LeadElement).HLoc;
9309  ExitPair.second = Track->TrackElementAt(1431, LeadElement).VLoc;
9310  Utilities->CallLogPop(2347);
9311  return(-1);
9312  }
9313  }
9314  }
9315 //here if LeadElement > -1 and its forward connection also > -1
9316 
9317  // calc distance to next red signal
9318  if(!Stopped() || RevisedStoppedAtLoc())
9319  {
9320  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
9321  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
9322  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
9323 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
9324  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
9325  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
9326  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
9327  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
9328  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
9329  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
9330  before the train has stopped the current station is still recognised as a future stop.
9331  In signaller mode stops don't count, and if pass stop signal command is given then when have LeadMidLag the current element
9332  becomes the signal, and the time to act indication becomes 'NOW'.
9333 */
9334  {
9335  FirstPosToBeMeasured = LeadElement;
9336  FirstEntryPos = LeadEntryPos;
9337  }
9338  float CurrentStopTime; // set to 0 at start of function
9339  float LaterStopTime; // set to 0 at start of function
9340  float RecoverableTime; // set to 0 at start of function
9341  int AvTrackSpeed; // set to zero at start of function
9342  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
9343  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
9344  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
9345 //at this point can't have both DistanceToRedSignal and DistanceToExit both set. Either both will be unset (-1) or one will be set.
9346 //Therefore since need to calculate the time for each in the same way use a generic
9347 
9348  if((DistanceToRedSignal == -1) && (DistanceToExit == -1))// both unset so no action needed
9349  {
9350  TimeToExit = -1;
9351  Utilities->CallLogPop(2076);
9352  return(-1);
9353  }
9354 //else one or other is set
9355  bool DistanceToRedSignalSet = (DistanceToRedSignal > -1);
9356  bool DistanceToExitSet = (DistanceToExit > -1);
9357  int GenericDistance = DistanceToRedSignal;
9358  if(DistanceToExitSet)
9359  {
9360  GenericDistance = DistanceToExit;
9361  }
9362 /* Have MinsDelayed; pos or neg,
9363  CurrentStopTime; pos or zero
9364  LaterStopTime; pos or zero
9365  RecoverableTime; pos or zero
9366 
9367  & from these calculate TotalStopTime. noting that:
9368  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
9369  RecoverableTime always < LaterStopTime or both zero
9370  can't subtract more than RecoverableTime (MinsDelayed > 0)
9371  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
9372  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
9373  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
9374  if running early & stopped at location CurrentStopTime will automatically include the excess
9375 */
9376  float TimeToSubtract, TotalStopTime;
9377  if(MinsDelayed > RecoverableTime)
9378  {
9379  TimeToSubtract = RecoverableTime;
9380  }
9381  else
9382  {
9383  TimeToSubtract = MinsDelayed; // may be negative;
9384  }
9385  if((AvTrackSpeed > 0) && (DistanceToStationStop <= GenericDistance) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
9386  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
9387  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
9388  //next station stop
9389  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
9390  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
9391  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
9392  //first find departure time from the next stop
9393  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
9394  {
9395  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
9396  {
9399  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
9400  }
9401  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
9402  {
9404  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
9405  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
9406  {
9407  // must be a departure
9408  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
9409  }
9410  }
9411  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
9412  {
9413  MinsEarly = 0;
9414  }
9415  if(MinsEarly < 0)
9416  {
9417  MinsEarly = 0;
9418  }
9419  }
9420  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
9421  {
9422  if(CurrentStopTime > 0)
9423  {
9424  TotalStopTime = CurrentStopTime + LaterStopTime;
9425  }
9426  // stopped at loc, will depart on time
9427  else
9428  {
9429  TotalStopTime = LaterStopTime - MinsDelayed;
9430  }
9431  // not stopped, will depart on time at first later stop so add the delay
9432  }
9433  else if((MinsEarly > 0) && !Stopped()) //running early
9434  {
9435  TotalStopTime = LaterStopTime + MinsEarly;
9436  }
9437  else // on time or running late
9438  {
9439  if(LaterStopTime == 0)
9440  {
9441  TotalStopTime = CurrentStopTime;
9442  }
9443  // no later stops, if stopped now will depart as soon as possible,
9444  // if not stopped no stop times to add
9445  else
9446  {
9447  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
9448  }
9449  }
9450  if(AvTrackSpeed < 30)
9451  {
9452  AvTrackSpeed = 30;
9453  }
9454  int Speed = AvTrackSpeed;
9455  if(AvTrackSpeed > int(MaxRunningSpeed))
9456  {
9457  Speed = int(MaxRunningSpeed);
9458  }
9459  if(TrainMode == Signaller)
9460  {
9461  Speed = SignallerMaxSpeed;
9462  TotalStopTime = 0;
9463  }
9464  if(DistanceToRedSignalSet)
9465  {
9466  TimeToAct = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9467  // accel & decel taken into account in
9468  // CalcDistanceToRedSignalandStopTime
9469  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
9470  TimeToExit = -1;
9471  Utilities->CallLogPop(2079);
9472  return(TimeToAct);
9473  }
9474  else //DistanceToExitSet must be true
9475  {
9476  TimeToExit = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9477  // accel & decel taken into account in
9478  // CalcDistanceToRedSignalandStopTime
9479  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
9480  Utilities->CallLogPop(2370);
9481  return(-1); //no red signal so no time to act
9482  }
9483  }
9484  else // stopped not at location
9485  {
9487  {
9488  TimeToAct = 0.0;
9489  TimeToExit = -1;
9490  }
9491  if(StoppedWithoutPower) //added at v2.13.2 as this situation was missed & time to act was 0 [If train failed then covered above]
9492  {
9493  TimeToAct = -1;
9494  TimeToExit = -1;
9495  }
9496  // but if stopped at a signal & autosigs route after it then ok, provided signal not failed
9497  if(StoppedAtSignal)
9498  {
9499  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
9500  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
9501  bool NextElementFailed = Track->TrackElementAt(1548, NextElement).Failed; //added at v2.13.2
9502  int NextExitPos;
9503  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
9504  {
9505  if((NextEntryPos == 0) || (NextEntryPos == 2))
9506  // leading entry point
9507  {
9508  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
9509  {
9510  NextExitPos = 1;
9511  }
9512  else
9513  {
9514  NextExitPos = 3;
9515  }
9516  }
9517  else
9518  {
9519  NextExitPos = 0; // trailing entry point
9520  }
9521  }
9522  else
9523  {
9524  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
9525  }
9526  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
9527  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
9528  int RouteNumber; // holder for referenced value, not used
9529  if((AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !NextElementFailed)
9530  { //NextElementFailed added at v2.13.2
9531  TimeToAct = -1;
9532  TimeToExit = -1;
9533  }
9534  }
9535  Utilities->CallLogPop(2074);
9536  return(TimeToAct);
9537  }
9538 }
9539 
9540 // ---------------------------------------------------------------------------
9541 
9543 {
9544  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
9545  if(LeadElement > -1)
9546  {
9548  {
9549  Utilities->CallLogPop(2148);
9550  return(true);
9551  }
9552  }
9553  if(MidElement > -1)
9554  {
9556  {
9557  Utilities->CallLogPop(2149);
9558  return(true);
9559  }
9560  }
9561  if(LagElement > -1)
9562  {
9564  {
9565  Utilities->CallLogPop(2150);
9566  return(true);
9567  }
9568  }
9569  Utilities->CallLogPop(2151);
9570  return(false);
9571 }
9572 
9573 // ---------------------------------------------------------------------------
9574 // TTrainController
9575 // ---------------------------------------------------------------------------
9576 
9578 {
9579  OnTimeArrivals = 0;
9580  LateArrivals = 0;
9581  EarlyArrivals = 0;
9582  OnTimePasses = 0;
9583  LatePasses = 0;
9584  EarlyPasses = 0;
9585  OnTimeExits = 0; //these 3 exits added at v2.9.2 - missed in error earlier
9586  LateExits = 0;
9587  EarlyExits = 0;
9588  OnTimeDeps = 0;
9589  LateDeps = 0;
9590  MissedStops = 0;
9591  OtherMissedEvents = 0;
9592  UnexpectedExits = 0;
9593  NumFailures = 0;
9594  IncorrectExits = 0;
9595  SPADEvents = 0;
9596  SPADRisks = 0;
9597  CrashedTrains = 0;
9598  Derailments = 0;
9599  TotArrDepPass = 0;
9600  TotLateArrMins = 0;
9601  TotEarlyArrMins = 0;
9602  TotLatePassMins = 0;
9603  TotEarlyPassMins = 0;
9604  TotLateExitMins = 0; //added at v2.9.1
9605  TotEarlyExitMins = 0; //added at v2.9.1
9606  TotLateDepMins = 0;
9607  ExcessLCDownMins = 0;
9608  SkippedTTEvents = 0; //added at v2.11.0
9609  TTClockTime = 0; // added for v0.6
9611  // added at v1.3.0 to ensure false at start
9612  OpTimeToActUpdateCounter = 0; // new v2.2.0
9613  OpActionPanelVisible = false; // new v2.2.0
9614  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
9615  SSHigh = false;
9616  MRSHigh = false;
9617  MRSLow = false;
9618  MassHigh = false;
9619  BFHigh = false;
9620  BFLow = false;
9621  PwrHigh = false;
9622  SigSHigh = false;
9623  SigSLow = false;
9624  randomize();
9625  // to seed rand() & random() with a random number (see UpdateTrain)
9626 }
9627 
9628 // ---------------------------------------------------------------------------
9629 
9631 {
9632  for(unsigned int x = 0; x < TrainVector.size(); x++)
9633  {
9634  TrainVectorAt(32, x).DeleteTrain(4);
9635  }
9636  TrainVector.clear();
9637 }
9638 
9639 // ---------------------------------------------------------------------------
9640 
9641 void TTrainController::LogEvent(AnsiString Str)
9642 {
9643  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
9644 
9645  // restrict to last 1000 entries
9646  Utilities->EventLog.push_back(FullStr);
9647  if(Utilities->EventLog.size() > 1000)
9648  {
9649  Utilities->EventLog.pop_front();
9650  }
9651 }
9652 
9653 // ---------------------------------------------------------------------------
9654 
9656 {
9657  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
9658  bool ClockState = Utilities->Clock2Stopped;
9659  Utilities->Clock2Stopped = true;
9660  // new section dealing with Snt & Snt-sh additions
9661  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
9662  // clock tick after stops flashing
9664  {
9665  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9666  {
9667  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
9668  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
9669  TActionEventType EventType = NoEvent;
9670  if(AVEntry0.Command == "Snt")
9671  {
9672  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9673  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9674  int IncrementalMinutes = 0;
9675  int IncrementalDigits = 0;
9676  if(AVEntryLast.FormatType == Repeat)
9677  {
9678  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9679  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9680  }
9681  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9682  {
9683  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
9684  }
9685  // see above note
9686 
9687  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9688  {
9689  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
9690  if(TTOD.RunningEntry != NotStarted)
9691  {
9692  continue;
9693  }
9694 
9695 //Multiplayer: here check for a train entering at a coupling {RearStartOrRepeatMins shows if it's a coupling or not), and can only be a Snt entry
9696 //if so and no arrival signalled yet bypass the timetabled arrival
9697 //if so and arrival signalled then start the new service, using the repeat number and headcode for the entering train
9698 //if a repeat is skipped then should be ok if it arrives later as its RunningEntry is still NotStarted
9699 
9700  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
9701  {
9702  break; // all the rest will also be greater
9703  }
9704  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
9705  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9706  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
9707  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
9708  {
9709  TTOD.TrainID = TrainVector.back().TrainID;
9710  TTOD.RunningEntry = Running;
9711  TrainVector.back().Description = TDEntry.FixedDescription; //added at v2.16.1
9712  }
9713  else if(EventType == FailTrainEntry)
9714  {
9715  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9716  }
9717  }
9718  }
9719  if(AVEntry0.Command == "Snt-sh")
9720  // just start this once, shuttle repeats take care of restarts
9721  {
9722  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9723  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9724  int IncrementalMinutes = 0;
9725  int IncrementalDigits = 0;
9726  if(AVEntryLast.FormatType == Repeat)
9727  {
9728  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9729  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9730  }
9731  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9732  {
9733  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
9734  }
9735  // see above note
9736  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
9737  if(TTOD.RunningEntry == NotStarted)
9738  {
9739  if(AVEntry0.EventTime <= TTClockTime)
9740  {
9741  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9742  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
9743  TDEntry.SignallerSpeed, false, EventType))
9744  // false for SignallerControl
9745  {
9746  TTOD.TrainID = TrainVector.back().TrainID;
9747  TTOD.RunningEntry = Running;
9748  TrainVector.back().Description = TDEntry.FixedDescription; //added at v2.16.1
9749  }
9750  else if(EventType == FailTrainEntry)
9751  {
9752  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9753  }
9754  }
9755  }
9756  }
9757  }
9758  }
9759 
9760  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
9761  // iteration, next cycle will catch up with any other pending updates
9762  if(!TrainVector.empty())
9763  {
9764  TrainAdded = false;
9765 
9766 //elapsed time investigations
9767 
9768 //elapsed time segment
9769 //double Start, End;
9770 //AnsiString ElapsedTimeReport = "";
9771 //end elasped time segment
9772  AllRoutes->CallonVector.clear(); // this will be rebuilt during the calls to UpdateTrain
9773 //elapsed time segment
9774 //PerfLogForm->PerformanceLog(-1, "\n Train vector size: " + AnsiString(TrainVector.size()) + '\n');
9775 //PerfLogForm->PerformanceLog(-1, "Start time list");
9776 //end elapsed time segment
9777  for(unsigned int x = 0; x < TrainVector.size(); x++)
9778  {
9779 //elapsed time segment
9780 //Start = double(GetTime()) * 86400; //secs
9781 //end elapsed time segment
9782  TrainVectorAt(33, x).UpdateTrain(0);
9783 //elapsed time segment
9784 //End = double(GetTime()) * 86400;
9785 //ElapsedTimeReport = TrainVectorAt(-1, x).TrainDataEntryPtr->ServiceReference + AnsiString(" ") + AnsiString(int((End - Start) * 1000)); //msecs
9786 //PerfLogForm->PerformanceLog(-1, ElapsedTimeReport);
9787 //end elapsed time segment
9788 
9789 //end elapsed time investigations
9790 
9791  /* added HasTrainGone() condition below in v0.4c to prevent 2 trains both having TrainGone set in UpdateTrain
9792  at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
9793  iterates in reverse to erase the second train to have gone (when the first train to have gone comes before the second in TrainVector),
9794  but afterwards ReplotTrains iterates forwards and therefore replots the first train to have gone and therefore sets the TrainIDOnElement value
9795  to the exited train, with nothing to reset it. Hovering the mouse over that element with train information enabled causes an error because
9796  the track element thinks the train is still there, whereas it is missing from the TrainVector. BUT subsequently (in v2.11.1) changed RePlotTrains
9797  so it doesn't plot trains with TrainGone set, but left this is as does no harm
9798 
9799  Had another error notified by Kevin Smith on 02/01/22 where a train was manually removed in the same clock cycle as a train exited, and this caused
9800  the same error as above. Did a lot of experimenting but eventually cured it with two changes, first as above in RePlotTrains, and also below adding
9801  a break; command after one TrainHasGone() dealt with. There were introduced in v2.11.1 & seems ok now
9802 
9803  These changes should deal with any number of TrainGone flags set in the same clock cycle - from exiting, manual removal, or joins
9804  */
9805  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
9806  {
9807  break; //only one exited train will be dealt with at a time (see below) so no point looking further
9808  }
9809  }
9810  // set warning flags (ManualLCDownAttentionWarning dealt with in InterfaceUnit)
9811  CrashWarning = false;
9812  DerailWarning = false;
9813  SPADWarning = false;
9814  CallOnWarning = false;
9815  SignalStopWarning = false;
9816  BufferAttentionWarning = false;
9817  TrainFailedWarning = false;
9818  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
9819  {
9820  TTrain &Train = TrainVectorAt(34, x);
9821  if(Train.Crashed)
9822  // can't use background colours for crashed & derailed because same colour
9823  {
9824  CrashWarning = true;
9825  }
9826  else if(Train.Derailed)
9827  // can't use background colours for crashed & derailed because same colour
9828  {
9829  DerailWarning = true;
9830  }
9831  else if(Train.BackgroundColour == clSPADBackground)
9832  // use colour as that changes as soon as passes signal
9833  {
9834  SPADWarning = true;
9835  }
9836  else if(Train.BackgroundColour == clTrainFailedBackground)
9837  {
9838  TrainFailedWarning = true;
9839  }
9840  else if(Train.BackgroundColour == clCallOnBackground)
9841  // use colour as also stopped at signal
9842  {
9843  CallOnWarning = true;
9844  }
9845  else if(Train.BackgroundColour == clSignalStopBackground)
9846  // use colour to distinguish from call-on
9847  {
9848  SignalStopWarning = true;
9849  }
9850  else if(Train.BackgroundColour == clBufferAttentionNeeded)
9851  // use colour to distinguish from ordinary buffer stop
9852  {
9853  BufferAttentionWarning = true;
9854  }
9855  if(Train.HasTrainGone())
9856  {
9857  AnsiString Loc = "";
9858  bool ElementFound = false;
9859  TTrackElement TE;
9860  if(Train.LagElement > -1)
9861  {
9862  TE = Track->TrackElementAt(531, Train.LagElement);
9863  ElementFound = true;
9864  }
9865  else if(Train.MidElement > -1)
9866  {
9867  TE = Track->TrackElementAt(779, Train.MidElement);
9868  ElementFound = true;
9869  }
9870  else if(Train.LeadElement > -1)
9871  {
9872  TE = Track->TrackElementAt(780, Train.LeadElement);
9873  ElementFound = true;
9874  }
9875  if(ElementFound)
9876  {
9877  if(TE.ActiveTrackElementName != "")
9878  {
9879  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9880  }
9881  else
9882  {
9883  Loc = "track element " + TE.ElementID;
9884  }
9885  }
9886  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
9887  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
9888  // need above first because may also have ActionVectorEntryPtr == "Fer"
9889  {
9890  Train.UnplotTrain(9);
9891  // added at v1.3.0 to reset signals after train removed from an autosigsroute
9893  {
9896  }
9897  // end of addition
9898  AllRoutes->RebuildRailwayFlag = true;
9899  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
9900  // correctly after a crash
9901  }
9902  else if(AVEntryPtr->Command == "Fer")
9903  {
9904  bool CorrectExit = false;
9905  if(!AVEntryPtr->ExitList.empty())
9906  {
9907  for(TNumListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
9908  {
9909  if(*ELIT == Train.LagElement)
9910  {
9911  CorrectExit = true;
9912  }
9913  }
9914  }
9915  if(CorrectExit)
9916  {
9917  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, "", AVEntryPtr->EventTime, AVEntryPtr->Warning);
9918  }
9919  else
9920  {
9921  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
9922  }
9923  }
9924  else
9925  {
9926  if(!AVEntryPtr->SignallerControl)
9927  {
9928  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
9929  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
9930  // -2 is marker for send messages for all remaining actions except Fer if present
9931  }
9932  else
9933  {
9934  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, "", TDateTime(0), false); // false for Warning
9935  }
9936  }
9937  Utilities->CumulativeDelayedRandMinsAllTrains += Train.CumulativeDelayedRandMinsOneTrain; //added at v2.13.0 for random delays
9938  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
9939  Train.DeleteTrain(1);
9940  TrainVector.erase(TrainVector.begin() + x);
9941  ReplotTrains(1, Display); //to reset ElementIDs for remaining trains when have removed a train
9942  //NB: won't plot any trains with TrainGone flag set (changed at v2.11.1)
9943  break; //added at v2.11.1 to ensure that only one train with TrainGone set is dealt with in one clock cycle
9944  }
9945  }
9946  }
9947  else
9948  {
9949  // reset all flags in case last train removed with flag set
9950  CrashWarning = false;
9951  DerailWarning = false;
9952  SPADWarning = false;
9953  CallOnWarning = false;
9954  SignalStopWarning = false;
9955  BufferAttentionWarning = false;
9956  TrainFailedWarning = false;
9957  }
9958  // update OpTimeToActMultimap
9960  {
9962  // clears entries then adds values for running trains then for continuation entries
9964  //added for multiplayer for running trains only
9965  }
9966  Utilities->Clock2Stopped = ClockState;
9967  Utilities->CallLogPop(723);
9968 }
9969 
9970 // ---------------------------------------------------------------------------
9972 {
9973  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
9974  if(!TrainVector.empty())
9975  {
9976  for(int x = TrainVector.size() - 1; x >= 0; x--)
9977  {
9978  TrainVectorAt(50, x).DeleteTrain(2);
9979  }
9980  TrainVector.clear();
9981  }
9982  if(!TrainDataVector.empty())
9983  {
9984  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9985  {
9986  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
9987  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9988  {
9989  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
9990  TOD.RunningEntry = NotStarted;
9991  TOD.TrainID = -1;
9992  TOD.EventReported = NoEvent;
9993  }
9994  }
9995  }
9996  Display->GetOutputLog1()->Caption = "";
9997  Display->GetOutputLog2()->Caption = "";
9998  Display->GetOutputLog3()->Caption = "";
9999  Display->GetOutputLog4()->Caption = "";
10000  Display->GetOutputLog5()->Caption = "";
10001  Display->GetOutputLog6()->Caption = "";
10002  Display->GetOutputLog7()->Caption = "";
10003  Display->GetOutputLog8()->Caption = "";
10004  Display->GetOutputLog9()->Caption = "";
10005  Display->GetOutputLog10()->Caption = "";
10006  Utilities->CallLogPop(1352);
10007 }
10008 
10009 // ---------------------------------------------------------------------------
10010 
10012 {
10013  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
10014  if(!TrainVector.empty())
10015  {
10016  for(unsigned int x = 0; x < TrainVector.size(); x++)
10017  {
10018  if(!TrainVectorAt(84, x).HasTrainGone()) //added at v2.11.0 to prevent plotting a train pending removal & particularly to prevent TrainElementID's being reinstated
10019  { //see Kevin Smith error information for details
10020  TrainVectorAt(51, x).PlotTrain(4, Disp);
10021  }
10022  }
10023  }
10024  Utilities->CallLogPop(724);
10025 }
10026 
10027 // ---------------------------------------------------------------------------
10028 
10029 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
10030 {
10031  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
10032  if(!TrainVector.empty())
10033  {
10034  for(unsigned int x = 0; x < TrainVector.size(); x++)
10035  {
10036  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
10037  }
10038  }
10039  Utilities->CallLogPop(1707);
10040 }
10041 
10042 // ---------------------------------------------------------------------------
10043 
10045 {
10046  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
10047  if(!TrainVector.empty())
10048  {
10049  for(unsigned int x = 0; x < TrainVector.size(); x++)
10050  {
10051  TrainVectorAt(52, x).UnplotTrain(10);
10052  }
10053  }
10055  Utilities->CallLogPop(725);
10056 }
10057 
10058 // ---------------------------------------------------------------------------
10059 
10060 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
10061  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
10062  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
10063 {
10064  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
10065  "," + AnsiString(Mass) + "," + ModeStr);
10066  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
10067  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr); //at v2.11.1 dropped later headcode - was listed twice
10068 
10069  int RearExitPos = -1;
10070 
10071  for(int x = 0; x < 4; x++)
10072  {
10073  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
10074  {
10075  RearExitPos = x;
10076  }
10077  }
10078  if(RearExitPos == -1)
10079  {
10080  throw Exception("Error, RearExit == -1 in AddTrain");
10081  }
10082  bool ReportFlag = true;
10083 
10084  // used to stop repeated messages from CheckStartAllowable when split failed
10085  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
10086  {
10087  ReportFlag = false;
10088  }
10089  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
10090  {
10091  // messages sent to performance log in CheckStartAllowable if ReportFlag true
10092  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
10093  Utilities->CallLogPop(938);
10094  return(false);
10095  }
10096  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
10097  TTrainMode TrainMode = NoMode;
10098 
10099  if(ModeStr == "Timetable")
10100  {
10101  TrainMode = Timetable;
10102  }
10103  // all else gives 'None', 'Signaller' set within program
10104 
10105  if(MaxRunningSpeed < 10)
10106  {
10107  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
10108  }
10109  if(SignallerSpeed < 10)
10110  {
10111  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
10112  }
10113  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
10114  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
10115 
10116  LogEvent("AddTrainSupplemental: Service Ref = " + TrainDataEntryPtr->ServiceReference + ", TrainID = " + AnsiString(NewTrain->TrainID)); //new at v2.11.1 so can relate headcode to ID
10117 
10118  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
10119  // initialise here rather than in TTrain constructor as create trains
10120  // with Null TrainDataEntryPtr when loading session trains
10121  if(SignallerControl)
10122  {
10123  NewTrain->TimetableFinished = true;
10124  NewTrain->SignallerStoppingFlag = false;
10125  NewTrain->TrainMode = Signaller;
10126  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
10127  {
10128  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
10129  }
10131  }
10132  // deal with starting conditions:-
10133  // unlocated Snt: just report entry & advance pointer
10134  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
10135  // Sns doesn't need a new train
10136  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
10137  // covers all above located starts
10138  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
10139  // wouldn't have accepted the timetable
10140  {
10141  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
10142  // StoppedAtBuffers is set in UpdateTrain()
10143  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
10144  // buffer end must be ahead of train or would have failed start position check
10145  {
10146  NewTrain->StoppedAtLocation = true;
10147  NewTrain->PlotStartPosition(0);
10149  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, "", NewTrain->ActionVectorEntryPtr->EventTime,
10150  NewTrain->ActionVectorEntryPtr->Warning);
10151  if(!SignallerControl) // don't advance if SignalControlEntry
10152  {
10153  NewTrain->ActionVectorEntryPtr++;
10154  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
10155  }
10156  NewTrain->LastActionTime = TTClockTime;
10157  }
10158  // else a through station stop
10159  else
10160  {
10161  NewTrain->StoppedAtLocation = true;
10162  NewTrain->PlotStartPosition(10);
10164  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, "", NewTrain->ActionVectorEntryPtr->EventTime,
10165  NewTrain->ActionVectorEntryPtr->Warning);
10166  if(!SignallerControl) // don't advance if SignalControlEntry
10167  {
10168  NewTrain->ActionVectorEntryPtr++;
10169  }
10170  NewTrain->LastActionTime = TTClockTime;
10171  }
10172  }
10173  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
10174  {
10175  NewTrain->PlotStartPosition(11);
10176  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
10177  AnsiString Loc = "";
10178  if(TE.ActiveTrackElementName != "")
10179  {
10180  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
10181  }
10182  else
10183  {
10184  Loc = "track element " + TE.ElementID;
10185  }
10186  if(TE.TrackType == Continuation)
10187  {
10188  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, "", NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
10189  }
10190  else
10191  {
10192  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, "", NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
10193  }
10194  if(!SignallerControl) // don't advance if SignalControlEntry
10195  {
10196  NewTrain->ActionVectorEntryPtr++;
10197  }
10198  NewTrain->LastActionTime = TTClockTime;
10199  // no need to set LastActionTime for an unlocated entry
10200  }
10201  // cancel a wrong-direction route if either element of train starts on one
10202  if(NewTrain->LeadElement > -1)
10203  {
10204  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
10205  }
10206  if(NewTrain->MidElement > -1)
10207  {
10208  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
10209  }
10210  // set signals for a right-direction autosigs route for either element of train on one
10211  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
10212  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
10213  int RouteNumber = -1;
10214  bool SignalsSet = false;
10215 
10216  if(NewTrain->LeadElement > -1)
10217  {
10218  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
10219  {
10220  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
10221  int RouteStartPosition;
10222  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
10223  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
10224  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
10225  if(FirstPair.first == RouteNumber)
10226  {
10227  RouteStartPosition = FirstPair.second;
10228  }
10229  else if(SecondPair.first == RouteNumber)
10230  {
10231  RouteStartPosition = SecondPair.second;
10232  }
10233  else
10234  {
10235  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
10236  }
10237  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
10238  SignalsSet = true;
10239  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
10240  }
10241  else if(RouteNumber > -1) // non-autosigsroute
10242  {
10243  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
10244  int FirstTVPos = TempPDE.GetTrackVectorPosition();
10245  int FirstELinkPos = TempPDE.GetELinkPos();
10246  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
10247  {
10248  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10249  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
10250  }
10251  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
10252  {
10253  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10254  // remove the last element under LeadElement
10255  }
10256  AllRoutes->RebuildRailwayFlag = true;
10257  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
10258  // now deal with a rear linked autosigs route
10259  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
10260  {
10261  int LinkedRouteNumber = -1;
10262  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
10263  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
10264  {
10265  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
10266  // this is ok as here we are setting signals from the start of the route
10267  }
10268  }
10269  SignalsSet = true;
10270  }
10271  }
10272  if(NewTrain->MidElement > -1)
10273  // if entering at a continuation MidElement == -1
10274  {
10275  // this is included in case a train starts with LeadElement on no route and MidElement on a route
10276  if(!SignalsSet)
10277  {
10278  RouteNumber = -1;
10279  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
10280  {
10281  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
10282  int RouteStartPosition;
10283  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
10284  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
10285  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
10286  if(FirstPair.first == RouteNumber)
10287  {
10288  RouteStartPosition = FirstPair.second;
10289  }
10290  else if(SecondPair.first == RouteNumber)
10291  {
10292  RouteStartPosition = SecondPair.second;
10293  }
10294  else
10295  {
10296  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
10297  }
10298  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
10299  SignalsSet = true;
10300  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
10301  }
10302  else if(RouteNumber > -1) // non-autosigsroute
10303  {
10304  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
10305  int FirstTVPos = TempPDE.GetTrackVectorPosition();
10306  int FirstELinkPos = TempPDE.GetELinkPos();
10307  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
10308  {
10309  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10310  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
10311  }
10312  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
10313  {
10314  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10315  // remove the last element under LeadElement
10316  }
10317  AllRoutes->RebuildRailwayFlag = true;
10318  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
10319  // now deal with a rear linked autosigs route
10320  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
10321  {
10322  int LinkedRouteNumber = -1;
10323  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
10324  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
10325  {
10326  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
10327  // this is ok as now we are setting signals from the start of the route
10328  }
10329  }
10330  }
10331  }
10332  }
10333  TrainVector.push_back(*NewTrain);
10334  Utilities->CallLogPop(731);
10335  return(true);
10336 }
10337 
10338 // ---------------------------------------------------------------------------
10339 
10340 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
10341 {
10342  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
10343  AnsiString(TrackVectorNumber));
10344  int VecPos = -1;
10345 
10346  for(unsigned int x = 0; x < TrainVector.size(); x++)
10347  {
10348  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
10349  {
10350  VecPos = x;
10351  }
10352  }
10353  if(VecPos == -1)
10354  {
10355  throw Exception("Error, VecPos not set in EntryPos");
10356  }
10357  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
10358  {
10359  Utilities->CallLogPop(734);
10360  return(TrainVectorAt(3, VecPos).LeadEntryPos);
10361  }
10362  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
10363  {
10364  Utilities->CallLogPop(735);
10365  return(TrainVectorAt(5, VecPos).MidEntryPos);
10366  }
10367  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
10368  {
10369  Utilities->CallLogPop(736);
10370  return(TrainVectorAt(7, VecPos).LagEntryPos);
10371  }
10372  Utilities->CallLogPop(737);
10373  return(-1);
10374 }
10375 
10376 // ---------------------------------------------------------------------------
10377 
10379 {
10380  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
10381  for(unsigned int x = 0; x < TrainVector.size(); x++)
10382  {
10383  if(TrainVectorAt(53, x).TrainID == TrainID)
10384  {
10385  Utilities->CallLogPop(738);
10386  return(TrainVectorAt(54, x));
10387  }
10388  }
10389  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
10390 }
10391 
10392 // ---------------------------------------------------------------------------
10393 
10394 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
10395 // return true if find the train (added at v2.4.0 as can select a removed train in
10396 // ActionsDueListBox before it updates - reported by LiWinDom in error report 23/04/20)
10397 {
10398  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
10399  for(unsigned int x = 0; x < TrainVector.size(); x++)
10400  {
10401  if(TrainVectorAt(69, x).TrainID == TrainID)
10402  {
10403  Utilities->CallLogPop(2152);
10404  return(true);
10405  }
10406  }
10407  Utilities->CallLogPop(2153);
10408  return(false);
10409 }
10410 
10411 // ---------------------------------------------------------------------------
10412 
10413 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
10414 {
10415  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
10416  Utilities->Format96HHMMSS(Time));
10417  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
10418 
10419  Utilities->CallLogPop(2061);
10420  return(RepeatTime);
10421 }
10422 
10423 // ---------------------------------------------------------------------------
10424 
10425 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
10426 // Enter with Ptr pointing to first action to be listed (i.e. next action)
10427 {
10428  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
10429  AnsiString RetStr = "", PartStr = "";
10430  int Count = 0;
10431  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
10432 
10433  Ptr--; // because incremented at start of loop
10434  do
10435  {
10436  Ptr++;
10437  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
10438  {
10439  continue; // move past the starting entry
10440  }
10441  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
10442  {
10443  break;
10444  }
10445  if(Ptr->SignallerControl)
10446  {
10447  RetStr = "Train under signaller control";
10448  break;
10449  }
10450  if(Ptr->FormatType == TimeTimeLoc)
10451  {
10452  if(Ptr->ArrivalTime == Ptr->DepartureTime)
10453  {
10454  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
10455  }
10456  else
10457  {
10458  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
10459  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10460  Count++; // because there are 2 entries
10461  }
10462  }
10463  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
10464  {
10465  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
10466  }
10467  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
10468  {
10469  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10470  }
10471  else if(Ptr->FormatType == PassTime) // new
10472  {
10473  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
10474  }
10475  else if(Ptr->Command == "Fns")
10476  {
10477  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10478  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10479  PartStr = ControllerGetNewServiceDepartureInfo(11, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
10480  }
10481  else if(Ptr->Command == "F-nshs")
10482  {
10483  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10484  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
10485  PartStr = ControllerGetNewServiceDepartureInfo(13, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10486  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
10487  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
10488  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
10489  }
10490 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
10491  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10492  {
10493  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10494  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10495  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10496  PartStr = ControllerGetNewServiceDepartureInfo(15, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10497  }
10498  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10499  {
10500  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10501  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
10502  PartStr = ControllerGetNewServiceDepartureInfo(17, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10503  }
10504  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10505  {
10506  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10507  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10508  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10509  PartStr = ControllerGetNewServiceDepartureInfo(19, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10510  }
10511  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10512  {
10513  PartStr = "Terminate at " + Ptr->LocationName;
10514  }
10515  else if(Ptr->Command == "Frh")
10516  {
10517  PartStr = "Terminate at " + Ptr->LocationName;
10518  }
10519  else if(Ptr->Command == "Fer")
10520  {
10521  AnsiString AllowedExits;
10522  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
10523  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
10524  }
10525  else if(Ptr->Command == "Fjo")
10526  {
10527  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
10528  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10529  }
10530  else if(Ptr->Command == "jbo")
10531  {
10532  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
10533  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10534  }
10535  else if(Ptr->Command == "fsp")
10536  {
10537  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
10538  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10539  }
10540  else if(Ptr->Command == "rsp")
10541  {
10542  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
10543  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10544  }
10545  else if(Ptr->Command == "cdt")
10546  {
10547  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
10548  }
10549  else if(Ptr->Command == "dsc")
10550  {
10551  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(27, Ptr->EventTime, RepNum, IncMins)) + ": Change description at " + Ptr->LocationName;
10552  }
10553  if(RetStr != "")
10554  {
10555  RetStr = RetStr + '\n' + PartStr;
10556  }
10557  else
10558  {
10559  RetStr = PartStr;
10560  }
10561  Count++;
10562  }
10563  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
10564  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
10565  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
10566  // forward as anyone should wish to see without looking at the full timetable
10567  Utilities->CallLogPop(2072);
10568  return(RetStr);
10569 }
10570 
10571 // ---------------------------------------------------------------------------
10572 
10573 AnsiString TTrainController::ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
10574 { //no delays as train not entered yet
10575  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - TDEPtr->ActionVector.begin()) + ","
10576  + AnsiString(RptNum) + ",ControllerGetNewServiceDepartureInfo," + TDEPtr->HeadCode);
10577  AnsiString DepTime = "", EventTime = "";
10578  bool CDTFlag = false; //reports if train changes direction before departs
10579  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
10580  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
10581  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
10582  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
10583  {
10584  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
10585  {
10586  TowardsLocation = AVI->LocationName;
10587  }
10588  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
10589  {
10590  TTrackElement TE = Track->TrackElementAt(1453, (AVI->ExitList.front()));
10591  if(TE.ActiveTrackElementName != "")
10592  {
10593  TowardsLocation = TE.ActiveTrackElementName;
10594  }
10595  else
10596  {
10597  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
10598  }
10599  }
10600  }
10601  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
10602  {
10603  if(AVI->Command == "cdt")
10604  {
10605  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
10606  continue;
10607  }
10608  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
10609  {
10610  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
10611  RetStr += "\nNew service splits at " + EventTime;
10612  Utilities->CallLogPop(2237);
10613  return(RetStr);
10614  }
10615  if(AVI->Command == "jbo")
10616  {
10617  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(30, AVI->EventTime, RptNum, IncrementalMinutes));
10618  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
10619  Utilities->CallLogPop(2238);
10620  return(RetStr);
10621  }
10622  if((AVI->Command == "Fns") || (AVI->Command == "F-nshs") || (AVI->Command == "Fns-sh"))
10623  {
10624  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(31, AVI->EventTime, RptNum, IncrementalMinutes));
10625  RetStr += "\nNew service finishes and forms another new service at " + EventTime;
10626  Utilities->CallLogPop(2607);
10627  return(RetStr);
10628  }
10629  if(AVI->Command == "Fjo")
10630  {
10631  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
10632  RetStr += "\nNew service finishes and joins " + AVI->OtherHeadCode + " at " + EventTime;
10633  Utilities->CallLogPop(2608);
10634  return(RetStr);
10635  }
10636  if(AVI->Command == "Frh")
10637  {
10638  RetStr += "\nNew service finishes and remains at location.";
10639  Utilities->CallLogPop(2609);
10640  return(RetStr);
10641  }
10642  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
10643  {
10644  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
10645  if(CDTFlag)
10646  {
10647  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10648  {
10649  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
10650  }
10651  else
10652  {
10653  RetStr += "\nNew service changes direction then departs at " + DepTime;
10654  }
10655  }
10656  else
10657  {
10658  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10659  {
10660  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
10661  }
10662  else
10663  {
10664  RetStr += "\nNew service departs at " + DepTime;
10665  }
10666  }
10667  Utilities->CallLogPop(2239);
10668  return(RetStr);
10669  }
10670  }
10671  Utilities->CallLogPop(2223);
10672  return(RetStr);
10673 }
10674 
10675 // ---------------------------------------------------------------------------
10676 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
10677 /*
10678  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
10679  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
10680  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
10681 
10682  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
10683  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
10684  user wishes
10685 
10686  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
10687  descriptive text or anything user wishes
10688  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
10689  be ignored) is taken as the timetable start time.
10690  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
10691  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
10692  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
10693  within the timetable if required.
10694  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
10695  services)
10696  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
10697  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
10698 
10699  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
10700  text timetable file easier
10701 
10702  form:-
10703  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
10704  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
10705  then multiple entries, separated by commas, of the form:-
10706 
10707  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
10708  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
10709  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
10710 
10711  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
10712  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
10713  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
10714 
10715  HH:MM;Command (cdt) }TimeCmd }
10716  HH:MM;Command;Description (dsc) }TimeCmdDescription }
10717  HH:MM;Location (arr & dep) }TimeLoc }
10718  HH:MM;HH:MM;Location }TimeTimeLoc }
10719  HH:MM;pas;Location }PassTime }
10720  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
10721  HH:MM;Fer;set of allowable IDs }ExitRailway }
10722  Command (Frh only) }FinRemHere }
10723 
10724  R;mm;dd;nn. Repeat Repeat entry
10725 
10726  Formats:
10727 
10728  Command only: Frh
10729  Time;Command: cdt
10730  Time;Command;Description dsc
10731  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
10732  Time;Command;2 Element IDs: Snt
10733  Time;Comand;n Element IDs: Fer
10734  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
10735  Time;Command;2 Element IDs;Headcode Snt-sh
10736  Time;Command;Location pas
10737  Time;Location Arr Dep
10738  Time;Time;Location Arr & dep together
10739 
10740  10 Non-linked entries: Snt (located or unlocated); pas; cdt; dsc; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
10741 
10742  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
10743  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
10744  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
10745 
10746  4 2x Linked entries, all shuttles:
10747 
10748  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
10749  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
10750  -> Remain Here (at finish location after all repeats)
10751  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
10752  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
10753 
10754  Moving/AtLoc states:-
10755 
10756  Successor state Type
10757 
10758  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/dsc/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
10759  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
10760  Sfs AtLoc )
10761  Sns AtLoc ) Start
10762  Sns-fsh AtLoc )
10763  Snt-sh AtLoc )
10764  Sns-sh AtLoc )
10765 
10766  pas Moving )
10767  jbo AtLoc )
10768  fsp AtLoc )
10769  rsp AtLoc ) Intermediate
10770  cdt AtLoc )
10771  dsc AtLoc )
10772  TimeLoc arr Moving (bef), AtLoc (aft) )
10773  TimeLoc dep AtLoc (bef), Moving (aft) )
10774  TimeTimeLoc Moving )
10775 
10776  Fns Repeat/Nothing)
10777  Fjo Repeat/Nothing)
10778  Frh Repeat/Nothing)
10779  Fer Repeat/Nothing) Finish
10780  Frh-sh Repeat )
10781  Fns-sh Repeat )
10782  F-nshs Nothing )
10783 
10784  Descriptions:
10785  Snt New train
10786  Sfs New service from split
10787  Sns New service from another service
10788  Sns-fsh New non-repeating service from a shuttle service
10789  Snt-sh New shuttle train at a timetabled stop
10790  Sns-sh New shuttle service from a feeder service
10791 
10792  pas Pass
10793  jbo Be joined by another train
10794  fsp Front split
10795  rsp Rear split
10796  cdt Change direction of train
10797  dsc Change description of train
10798  TimeLoc arr Arrival
10799  TimeLoc dep Departure
10800  TimeTimeLoc Arrival and departure
10801 
10802  Fns Finish & form a new service
10803  Fjo Finish & join another train
10804  Frh Finish & remain here
10805  Fer Finish & exit railway
10806  Frh-sh Finish & repeat shuttle, finally remain here
10807  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
10808  F-nshs Finish & form a shuttle feeder service
10809 */
10810 
10811 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
10812 {
10813  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
10814  // a line that is too long; timetable containing too few lines; and timetable failed to open.
10815  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
10816  // new for v0.2b
10817  // compile ActiveTrackElementNameMap
10818  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
10819 
10821  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
10822  {
10823  // if((Track->TrackElementAt(, x).ActiveTrackElementName != "") && (Track->TrackElementAt(, x).TrackType != Continuation))
10825  == Track->ContinuationNameMap.end())
10826  {
10827  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
10828  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1035, x).ActiveTrackElementName;
10829  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
10830  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
10831  }
10832  }
10834  // end of new section
10835  std::ifstream TTBLFile(FileName, std::ios_base::binary);
10836 
10837  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
10838  if(TTBLFile.is_open())
10839  {
10840  char *TrainTimetableString = new char[10000];
10841  // enough for over 200 stations, should be adequate!
10842  bool EndOfFile = false;
10843  int Count = 0;
10844  // counts 'relevant' lines, i.e ignores any before the start time on its own line
10845  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10846  // delimiter is '\0' as it's an AnsiString
10847  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10848  // file empty - stores a null in 1st position if doesn't load any characters
10849  {
10850  // may still have eof even if read a line (no CRLF at end), and
10851  // if so need to process it
10852  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
10853  TTBLFile.close();
10854  delete[] TrainTimetableString;
10855  Utilities->CallLogPop(1611);
10856  return(false);
10857  }
10858  AnsiString OneLine(TrainTimetableString);
10859  bool FinalCallFalse = false;
10860  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10861  // get rid of lines before the start time
10862  {
10863  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
10864  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10865  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10866  // stores a null in 1st position if doesn't load any characters
10867  {
10868  // may still have eof even if read a line (no CRLF at end), and
10869  // if so need to process it
10870  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
10871  TTBLFile.close();
10872  delete[] TrainTimetableString;
10873  Utilities->CallLogPop(772);
10874  return(false);
10875  }
10876  OneLine = AnsiString(TrainTimetableString);
10877  }
10878  // here when have accepted the start time
10879  Count++; // increment past the start time
10880  while(!EndOfFile)
10881  {
10882  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10883  // get next line after start time
10884  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10885  // stores a null in 1st position if doesn't load any characters
10886  {
10887  // may still have eof even if read a line (no CRLF at end), and
10888  // if so need to process it
10889  EndOfFile = true;
10890  OneLine = "";
10891  }
10892  else
10893  {
10894  OneLine = AnsiString(TrainTimetableString);
10895  }
10896  if(OneLine.Length() > 9999)
10897  {
10898  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
10899  TTBLFile.close();
10900  delete[] TrainTimetableString;
10901  Utilities->CallLogPop(789);
10902  return(false);
10903  }
10904  bool FinalCallFalse = false;
10905  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10906  // false for FinalCall - just checking at this stage
10907  {
10908  TTBLFile.close();
10909  delete[] TrainTimetableString;
10910  Utilities->CallLogPop(770);
10911  return(false);
10912  }
10913  if(EndOfFile && (Count < 2))
10914  // Timetable must contain at least two relevant lines, one for start time and at least one train
10915  {
10916  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
10917  TTBLFile.close();
10918  delete[] TrainTimetableString;
10919  Utilities->CallLogPop(771);
10920  return(false);
10921  }
10922  Count++;
10923  }
10924  delete[] TrainTimetableString;
10925  TTBLFile.close();
10926  } // if(TTBLFile.is_open())
10927  else
10928  {
10929  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
10930  Utilities->CallLogPop(2154);
10931  return(false);
10932  }
10933  Utilities->CallLogPop(753);
10934  return(true);
10935 }
10936 
10937 // ---------------------------------------------------------------------------
10938 
10939 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
10940  bool CheckLocationsExistInRailway) // return true for success
10941 
10942 /* Format:
10943  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
10944  descriptive text or anything user wishes
10945  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
10946  be ignored) is taken as the timetable start time.
10947  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
10948  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
10949  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
10950  within the timetable if required.
10951  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
10952  services)
10953  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
10954  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
10955 
10956  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
10957  text timetable file easier
10958 
10959  form:-
10960  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
10961  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
10962  then multiple entries, separated by commas, of the form:-
10963 
10964  Format FormatType
10965  [W]HH:MM;Command (cdt) }TimeCmd }
10966  [W]HH:MM;dsc;new description }TimeCmdDescription }
10967  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
10968  [W]HH:MM;pas;Location }PassTime }
10969  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
10970  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
10971  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
10972  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
10973  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
10974  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
10975  [W]HH:MM;Fns-sh;Details }FSHNewService }
10976  [W]HH:MM;Location (arr & dep) }TimeLoc }
10977  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
10978  Command (Frh only) }FinRemHere }
10979 
10980  R;mm;dd;nn. Repeat Repeat entry
10981 
10982  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
10983  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
10984  at location; or (c) departure time if train already at location (including train started at location either as a new
10985  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
10986  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
10987  minutes, incremental train headcode last 2 digits, and number of repeats.
10988 
10989  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
10990  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
10991  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
10992  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
10993  (it's for a shuttle train to return to depot at end of services)
10994 
10995  Command/Location & details are as follows:-
10996 
10997  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
10998  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
10999  2E44 in its Sfs entry. All these are checked.
11000  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
11001 
11002  Start commands:-
11003  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
11004  with loc as a start entry can't have a location as details)
11005  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
11006  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
11007  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
11008  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
11009  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
11010  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
11011  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
11012  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
11013  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
11014  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
11015  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
11016 
11017  Intermediate commands:-
11018  Time - Location (TimeLoc), can be arrival or departure depending on context
11019  Time Time location (TimeTimeLoc), arrival and departure
11020  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
11021  pas (PassTime), Time;pas;Location
11022  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
11023  joining train's finish details must correspond or the file check will fail
11024  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
11025  new train - that train's starting information must correspond)
11026  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
11027  new train - that train's starting information must correspond)
11028  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
11029  dsc (TimeCmdDescription) = Change description of train = new description only, no train creation
11030 
11031  Finish commands:-
11032  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
11033  creation)
11034  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
11035  shuttle headcode (no train creation)
11036  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
11037  may have to wait for it), details = new headcode (delete train)
11038  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
11039  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
11040  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
11041  here
11042  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
11043  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
11044 
11045  Repeat:-
11046  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
11047  headcodes - it is up to user to avoid duplicates if he/she wishes to.
11048 
11049  Checks carried out with error messages in this function:-
11050  At least one comma in a service line (it's based on a .csv file)
11051  No entries following train information;
11052  At least one comma in remainder after train information (i.e at least a start and a finish entry);
11053  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
11054  First entry not a start entry;
11055  Train information incomplete before a start entry;
11056  Entry follows a finish entry but doesn't begin with 'R';
11057  SplitEntry returns false in a finish entry - message repeats the entry for information;
11058  Last action entry isn't a finish entry.
11059 
11060  Function returns false with no message if:-
11061  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
11062  time is found at all then an error message is given in the calling function);
11063  SplitTrainInfo returns false (message given in called function);
11064  SplitRepeat returns false (message given in called function).
11065 */
11066 {
11067  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
11068  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
11069  TTrainDataEntry TempTrainDataEntry;
11070 
11071  EndOfFile = false;
11072  StripSpaces(0, OneLine);
11073  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
11074  // semicolons within the line
11075  ServiceReference = "";
11076  if(OneLine != "")
11077  {
11078  if(OneLine[1] != '*')
11079  {
11080  int SCPos = OneLine.Pos(';');
11081  if(SCPos == 0)
11082  {
11083  ServiceReference = OneLine.SubString(1, 8);
11084  }
11085  else
11086  {
11087  ServiceReference = OneLine.SubString(1, (SCPos - 1));
11088  }
11089  }
11090  }
11091  bool AllCommas = true;
11092 
11093  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
11094  {
11095  if(OneLine[x] != ',')
11096  {
11097  AllCommas = false;
11098  }
11099  }
11100  if(AllCommas || (OneLine == ""))
11101  {
11102  if(Count > 0)
11103  {
11104  EndOfFile = true;
11105  // returns true for a blank line (or a line of all commas) - treated as end of file
11106  Utilities->CallLogPop(1018);
11107  return(true);
11108  }
11109  else // count == 0 so not yet found a start time, no message to be given
11110  {
11111  Utilities->CallLogPop(754);
11112  return(false);
11113  }
11114  }
11115  AnsiString First = "", Second = "", Third = "", Fourth = "";
11116  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
11117  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
11118  TDateTime StartTime(0);
11119  TNumList ExitList;
11120  bool Warning = false;
11121 
11122  if(Count == 0) // no start time found yet
11123  {
11124 /* dropped at v0.6b
11125  AnyHeadCodeValid = false;
11126  if(OneLine.SubString(6,5) == ";0000")
11127  {
11128  AnyHeadCodeValid = true;
11129  }
11130 */
11131  if(!CheckTimeValidity(0, OneLine, StartTime))
11132  {
11133  // no message is given for an invalid time as it's assumed to be an irrelevant line
11134  // if no start time is found at all then an error message is given in the calling function
11135  // AnyHeadCodeValid = false;
11136  Utilities->CallLogPop(755);
11137  return(false);
11138  }
11139  if(FinalCall) // here if start time valid
11140  {
11141  TTClockTime = StartTime;
11142  TimetableStartTime = StartTime;
11143  }
11144  }
11145  else
11146  {
11147  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
11148  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
11149  double MaxBrakeRate = 0;
11150  double PowerAtRail = 0;
11151  int SignallerSpeed = 0;
11152  if(OneLine[1] == '*')
11153  {
11154  Utilities->CallLogPop(1581);
11155  return(true);
11156  // ignore any line beginning with '*' but return true as there is no error
11157  }
11158  int Pos = OneLine.Pos(',');
11159  if(Pos == 0)
11160  {
11161  int SubStringLength = 20;
11162  if(OneLine.Length() < 20)
11163  {
11164  SubStringLength = OneLine.Length();
11165  }
11166  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
11167  Utilities->CallLogPop(766);
11168  return(false);
11169  }
11170  TrainInfoStr = OneLine.SubString(1, Pos - 1);
11171  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
11172  GiveMessages)) // error messages given in SplitTrainInfo
11173  {
11174  Utilities->CallLogPop(773);
11175  return(false);
11176  }
11177  if(FinalCall)
11178  {
11179  // store Train info - conversions done in SplitTrainInfo
11180  // only headcode mandatory for continuing services
11181  //HeadCode = ServiceReference until final section of SecondPassActions
11182  TempTrainDataEntry.HeadCode = HeadCode;
11183  TempTrainDataEntry.ServiceReference = HeadCode;
11184  TempTrainDataEntry.FixedDescription = Description; //name changed at v2.16.1
11185  TempTrainDataEntry.ExplicitDescription = false;
11186  if(Description != "")
11187  {
11188  TempTrainDataEntry.ExplicitDescription = true;
11189  }
11190  TempTrainDataEntry.StartSpeed = StartSpeed;
11191  TempTrainDataEntry.Mass = Mass;
11192  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
11193  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
11194  TempTrainDataEntry.PowerAtRail = PowerAtRail;
11195  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
11196  TTrainOperatingData TempTrainOperatingData;
11197  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
11198  }
11199  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
11200  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
11201  // so strip them off
11202  while(NewRemainder[NewRemainder.Length()] == ',')
11203  {
11204  if(NewRemainder.Length() > 1)
11205  {
11206  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
11207  }
11208  else
11209  {
11210  NewRemainder = "";
11211  break;
11212  }
11213  }
11214  // check if zero length & fail if so
11215  if(NewRemainder == "")
11216  {
11217  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
11218  Utilities->CallLogPop(769);
11219  return(false);
11220  }
11221  // now have one more entry than there are commas
11222  int CommaCount = 0;
11223  for(int x = 1; x < NewRemainder.Length() + 1; x++)
11224  {
11225  if(NewRemainder[x] == ',')
11226  {
11227  CommaCount++;
11228  }
11229  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
11230  if(CommaCount == 0)
11231  {
11232  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
11233  {
11234  int SubStringLength = 20;
11235  if(OneLine.Length() < 20)
11236  {
11237  SubStringLength = OneLine.Length();
11238  }
11239  TimetableMessage(GiveMessages,
11240  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
11241  OneLine.SubString(1, SubStringLength) + "'....");
11242  Utilities->CallLogPop(783);
11243  return(false);
11244  }
11245  }
11246  AnsiString OneEntry = "";
11247  TTimetableFormatType FormatType;
11248  TTimetableSequenceType SequenceType;
11249  TTimetableLocationType LocationType;
11250  TTimetableShuttleLinkType ShuttleLinkType;
11251  bool FinishFlag = false;
11252  bool NewTrain = false;//added at v2.14.0 to record created trains for later zero power checks
11253  for(int x = 0; x < CommaCount + 1; x++)
11254  {
11255  if((CommaCount == 0) || (x < CommaCount))
11256  // i.e. train entered under signaller control with no repeats, or entry is not the last,
11257  // in which case there's a comma & finish element or repeat still to come this entry could
11258  // be a finish but can't be a repeat
11259  {
11260  if(CommaCount == 0)
11261  {
11262  OneEntry = NewRemainder;
11263  NewRemainder = "";
11264  }
11265  else
11266  {
11267  Pos = NewRemainder.Pos(',');
11268  OneEntry = NewRemainder.SubString(1, Pos - 1);
11269  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
11270  }
11271  First = "";
11272  Second = "";
11273  Third = "";
11274  Fourth = "";
11275  RearStartOrRepeatMins = 0;
11276  FrontStartOrRepeatDigits = 0;
11277  NumberOfRepeats = 0;
11278  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
11279  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
11280  {
11281  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
11282  Utilities->CallLogPop(756);
11283  return(false);
11284  }
11285  if((Second == "Snt") || (Second == "Snt-sh")) //added at v2.14.0, see above
11286  {
11287  NewTrain = true;
11288  }
11289  // check if warning for Frh or Fjo & reject
11290  if(Warning && (Second == "Frh"))
11291  {
11292  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
11293  Utilities->CallLogPop(1793);
11294  return(false);
11295  }
11296  if(Warning && (Second == "Fjo"))
11297  {
11298  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11299  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
11300  Utilities->CallLogPop(1794);
11301  return(false);
11302  }
11303  //below added at v2.14.0 to prevent unpowered trains attempting to be joined by (Second == jbo), split (Second -- fsp or rsp),
11304  //or change direction. Form a new service dealt with below for zero power as it's a finish event.
11305  if(NewTrain && (PowerAtRail < 1) && (Second == "jbo"))
11306  {
11307  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11308  "': a train created without power can't 'be joined by' another train (i.e. can't include command 'jbo'), "
11309  "use command 'Fjo' (i.e. 'join' another train) instead immediately after the line containing 'Snt', and use "
11310  "command 'jbo' for the train it is to join.");
11311  Utilities->CallLogPop(2545);
11312  return(false);
11313  }
11314  if(NewTrain && (PowerAtRail < 1) && ((Second == "fsp") || (Second == "rsp")))
11315  {
11316  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11317  "': a train created without power can't split.");
11318  Utilities->CallLogPop(2546);
11319  return(false);
11320  }
11321  if(NewTrain && (PowerAtRail < 1) && (Second == "cdt"))
11322  {
11323  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11324  "': a train created without power can't change direction under timetable control.");
11325  Utilities->CallLogPop(2547);
11326  return(false);
11327  }
11328  //end of new additions
11329  if(x == 0) // should be start event
11330  {
11331  if(SequenceType != StartSequence)
11332  {
11333  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
11334  Utilities->CallLogPop(784);
11335  return(false);
11336  }
11337  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
11338  {
11339  if(NewRemainder[1] != 'R')
11340  {
11341  TimetableMessage(GiveMessages,
11342  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
11343  OneEntry + "'");
11344  Utilities->CallLogPop(787);
11345  return(false);
11346  }
11347  }
11348  if((Second == "Snt") || (Second == "Snt-sh"))
11349  // need full train information including non-default values for at least HeadCode, Description,
11350  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
11351  {
11352  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
11353  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
11354  {
11355  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
11356  OneEntry + "'");
11357  Utilities->CallLogPop(1783);
11358  return(false);
11359  }
11360  }
11361  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
11362  // service continuation - need at least non-default value for HeadCode
11363  {
11364  if(HeadCode == "")
11365  {
11366  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
11367  OneEntry + "'");
11368  Utilities->CallLogPop(788);
11369  return(false);
11370  }
11371  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
11372  {
11373  TimetableMessage(GiveMessages,
11374  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
11375  OneEntry + "'");
11376  Utilities->CallLogPop(843);
11377  return(false);
11378  }
11379  }
11380  }
11381  if(SequenceType == FinishSequence)
11382  {
11383  FinishFlag = true;
11384  // marker for only permitted additional entry being a repeat, only needed if the
11385  // finish entry is not the last entry
11386  }
11387  if(FinalCall)
11388  {
11389  // interpret & add to ActionVector
11390  TDateTime TempTime;
11391  TActionVectorEntry ActionVectorEntry;
11392  ActionVectorEntry.FormatType = FormatType;
11393  ActionVectorEntry.LocationType = LocationType;
11394  ActionVectorEntry.SequenceType = SequenceType;
11395  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
11396  ActionVectorEntry.Warning = Warning;
11397  if(FormatType == TimeLoc)
11398  {
11399  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
11400  {
11401  ;
11402  } // these will all be true as final call
11403 
11404  ActionVectorEntry.LocationName = Second;
11405  }
11406  else if(FormatType == PassTime) // new
11407  {
11408  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
11409  {
11410  ;
11411  }
11412  ActionVectorEntry.Command = Second;
11413  ActionVectorEntry.LocationName = Third;
11414  }
11415  else if(FormatType == TimeTimeLoc)
11416  {
11417  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
11418  {
11419  ;
11420  }
11421  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
11422  {
11423  ;
11424  }
11425  ActionVectorEntry.LocationName = Third;
11426  }
11427  else if(FormatType == TimeCmd)
11428  {
11429  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
11430  {
11431  ;
11432  }
11433  ActionVectorEntry.Command = Second;
11434  }
11435  else if(FormatType == ExitRailway)
11436  {
11437  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
11438  {
11439  ;
11440  }
11441  ActionVectorEntry.Command = Second;
11442  ActionVectorEntry.ExitList = ExitList;
11443  }
11444  else if(FormatType == StartNew)
11445  {
11446  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
11447  {
11448  ;
11449  }
11450  ActionVectorEntry.Command = Second;
11451  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11452  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11453  if(Fourth == 'S')
11454  {
11455  ActionVectorEntry.SignallerControl = true;
11456  }
11457  }
11458  else if(FormatType == SNTShuttle)
11459  {
11460  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
11461  {
11462  ;
11463  }
11464  ActionVectorEntry.Command = Second;
11465  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11466  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11467  ActionVectorEntry.OtherHeadCode = Fourth;
11468  }
11469  else if(FormatType == SNSShuttle)
11470  {
11471  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
11472  {
11473  ;
11474  }
11475  ActionVectorEntry.Command = Second;
11476  ActionVectorEntry.OtherHeadCode = Third;
11477  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11478  }
11479  else if(FormatType == TimeCmdHeadCode) //fsp/rsp is TimeCmdHeadCode but may have a Fourth - see below
11480  {
11481  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
11482  {
11483  ;
11484  }
11485  ActionVectorEntry.Command = Second;
11486  ActionVectorEntry.OtherHeadCode = Third;
11487  if((Second == "fsp") || (Second == "rsp")) //new at v2.15.0 & checked in SplitEntry
11488  {
11489  ActionVectorEntry.SplitDistribution = "50-50"; //added at v2.18.0
11490  if(Fourth != "")
11491  {
11492  ActionVectorEntry.SplitDistribution = Fourth;
11493  }
11494  }
11495  }
11496  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
11497  {
11498  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
11499  {
11500  ;
11501  }
11502  ActionVectorEntry.Command = Second;
11503  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11504  }
11505  else if(FormatType == FSHNewService)
11506  {
11507  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
11508  {
11509  ;
11510  }
11511  ActionVectorEntry.Command = Second;
11512  ActionVectorEntry.OtherHeadCode = Third;
11513  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11514  }
11515  else if(FormatType == FinRemHere)
11516  {
11517  ActionVectorEntry.Command = Second;
11518  }
11519  else if(FormatType == TimeCmdDescription) //new at v2.15.0
11520  {
11521  if(CheckTimeValidity(35, First, ActionVectorEntry.EventTime))
11522  {
11523  ;
11524  }
11525  ActionVectorEntry.Command = Second;
11526  ActionVectorEntry.NewDescription = Third;
11527  }
11528  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11529  }
11530  }
11531  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
11532  {
11533  OneEntry = NewRemainder;
11534  First = "";
11535  Second = "";
11536  Third = "";
11537  Fourth = "";
11538  RearStartOrRepeatMins = 0;
11539  FrontStartOrRepeatDigits = 0;
11540  NumberOfRepeats = 0;
11541  if((FinishFlag) && (OneEntry[1] != 'R'))
11542  // already had a finish entry
11543  {
11544  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
11545  Utilities->CallLogPop(79);
11546  return(false);
11547  }
11548  if(OneEntry[1] != 'R') // must be finish
11549  {
11550  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
11551  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
11552  {
11553  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
11554  Utilities->CallLogPop(757);
11555  return(false);
11556  }
11557  //below added at v2.14.0 to prevent unpowered trains attempting to form a new service.
11558  if(NewTrain && (PowerAtRail < 1) && ((Second == "Fns") || (Second == "Frh-sh") || (Second == "Fns-sh") || (Second == "F-nshs")))
11559  {
11560  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11561  "': a train created without power can't form a new service.");
11562  Utilities->CallLogPop(2548);
11563  return(false);
11564  }
11565  //end of new additions
11566  if(SequenceType != FinishSequence)
11567  {
11568  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
11569  Utilities->CallLogPop(785);
11570  return(false);
11571  }
11572  if(FinalCall)
11573  {
11574  // interpret & add to ActionVector
11575  TDateTime TempTime;
11576  TActionVectorEntry ActionVectorEntry;
11577  ActionVectorEntry.FormatType = FormatType;
11578  ActionVectorEntry.LocationType = LocationType;
11579  ActionVectorEntry.SequenceType = SequenceType;
11580  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
11581  ActionVectorEntry.Warning = Warning;
11582  if(FormatType == TimeCmd)
11583  {
11584  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
11585  {
11586  ;
11587  }
11588  ActionVectorEntry.Command = Second;
11589  }
11590  else if(FormatType == TimeCmdHeadCode)
11591  {
11592  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
11593  {
11594  ;
11595  }
11596  ActionVectorEntry.Command = Second;
11597  ActionVectorEntry.OtherHeadCode = Third;
11598  }
11599  else if(FormatType == FNSNonRepeatToShuttle)
11600  {
11601  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
11602  {
11603  ;
11604  }
11605  ActionVectorEntry.Command = Second;
11606  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11607  }
11608  else if(FormatType == FSHNewService)
11609  {
11610  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
11611  {
11612  ;
11613  }
11614  ActionVectorEntry.Command = Second;
11615  ActionVectorEntry.OtherHeadCode = Third;
11616  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11617  }
11618  else if(FormatType == ExitRailway)
11619  {
11620  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
11621  {
11622  ;
11623  }
11624  ActionVectorEntry.Command = Second;
11625  ActionVectorEntry.ExitList = ExitList;
11626  }
11627  else if(FormatType == FinRemHere)
11628  {
11629  ActionVectorEntry.Command = Second;
11630  }
11631  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11632  }
11633  }
11634  else // repeat
11635  {
11636  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
11637  {
11638  Utilities->CallLogPop(786);
11639  // error messages given in SplitRepeat
11640  return(false);
11641  }
11642  if(FinalCall)
11643  {
11644  TActionVectorEntry ActionVectorEntry;
11645  ActionVectorEntry.FormatType = Repeat;
11646  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
11647  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
11648  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
11649  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11650  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11651  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
11652  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11653  }
11654  }
11655  }
11656  }
11657  if(FinalCall)
11658  {
11659  TrainDataVector.push_back(TempTrainDataEntry);
11660  }
11661  }
11662  Utilities->CallLogPop(80);
11663  return(true);
11664 }
11665 
11666 // ---------------------------------------------------------------------------
11667 
11668 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
11669 {
11670  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
11671  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
11672  {
11673  Utilities->CallLogPop(1890);
11674  return(false);
11675  }
11676  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
11677  {
11678  Utilities->CallLogPop(1891);
11679  return(false);
11680  }
11681  Utilities->CallLogPop(1892);
11682  return(true);
11683 }
11684 
11685 // ---------------------------------------------------------------------------
11686 
11687 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
11688 // 1st 5 chars must be HH:MM, anything else will be ignored
11689 {
11690  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
11691  if(TimeStr.Length() < 5)
11692  {
11693  Utilities->CallLogPop(926);
11694  return(false);
11695  }
11696  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
11697  {
11698  Utilities->CallLogPop(927);
11699  return(false);
11700  }
11701  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
11702  {
11703  Utilities->CallLogPop(928);
11704  return(false);
11705  }
11706  if(TimeStr[3] != ':')
11707  {
11708  Utilities->CallLogPop(929);
11709  return(false);
11710  }
11711  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
11712  {
11713  Utilities->CallLogPop(930);
11714  return(false);
11715  }
11716  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
11717  {
11718  Utilities->CallLogPop(931);
11719  return(false);
11720  }
11721  while(TimeStr.Length() > 5)
11722  {
11723  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
11724  }
11725  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
11726  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
11727 
11728  if((WholeHours + FracHour) >= 95.98334)
11729  {
11730  Utilities->CallLogPop(1817);
11731  return(false); // > 95h 59m
11732  }
11733  Time = TDateTime((WholeHours + FracHour) / 24);
11734  Utilities->CallLogPop(932);
11735  return(true);
11736 }
11737 
11738 // ---------------------------------------------------------------------------
11739 
11740 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
11741  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
11742  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
11743 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
11744  Return false for failure.
11745  See description above under ProcessOneTimetableLinefor details of train action entries
11746  NB all types set except LocationType for Snt as may be located or not
11747 */{
11748  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
11749  Warning = false;
11750  TDateTime TempTime;
11751 
11752  if(OneEntry.Length() > 0)
11753  {
11754  if(OneEntry[1] == 'W') // warning
11755  {
11756  Warning = true;
11757  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
11758  // strip it off
11759  }
11760  }
11761  if(OneEntry == "Frh")
11762  {
11763  FormatType = FinRemHere;
11764  SequenceType = FinishSequence;
11765  LocationType = AtLocation;
11766  ShuttleLinkType = NotAShuttleLink;
11767  Second = "Frh";
11768  Utilities->CallLogPop(1016);
11769  return(true);
11770  }
11771  if(OneEntry.Length() < 7)
11772  {
11773  Utilities->CallLogPop(907);
11774  return(false); // 'HH:MM;' + at least a one-letter location name
11775  }
11776  int Pos = OneEntry.Pos(';'); // first segment delimiter
11777 
11778  if(Pos != 6)
11779  {
11780  Utilities->CallLogPop(908);
11781  return(false);
11782  // no delimiter or delimiter not in position 6, has to be a time so fail
11783  }
11784  First = OneEntry.SubString(1, 5); // has to be a time
11785  if(!CheckTimeValidity(16, First, TempTime))
11786  {
11787  Utilities->CallLogPop(909);
11788  return(false);
11789  }
11790  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
11791 
11792 // if((Remainder[1] >= '0') && (Remainder[1] <= '9')) changed at v2.16.0 so only 'digit-digit-colon....' interpreted as a time - to allow locations to begin with digits
11793  if((Remainder.Length() >= 3) && (Remainder[1] >= '0') && (Remainder[1] <= '9') && (Remainder[2] >= '0') && (Remainder[2] <= '9') && (Remainder[3] == ':'))
11794  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
11795  {
11796  if(Remainder.Length() < 7)
11797  {
11798  Utilities->CallLogPop(910);
11799  return(false); // 'HH:MM;' + at least a one-letter location name
11800  }
11801  Pos = Remainder.Pos(';'); // second segment delimiter
11802  if(Pos == 0)
11803  {
11804  Utilities->CallLogPop(911);
11805  return(false);
11806  // no delimiter, has to be one between departure time & location
11807  }
11808  Second = Remainder.SubString(1, 5); // has to be a time
11809  if(!CheckTimeValidity(15, Second, TempTime))
11810  {
11811  Utilities->CallLogPop(912);
11812  return(false);
11813  }
11814  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11815  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
11816  {
11817  Utilities->CallLogPop(913);
11818  return(false);
11819  }
11820  FormatType = TimeTimeLoc;
11821  SequenceType = IntermediateSequence;
11822  LocationType = AtLocation;
11823  ShuttleLinkType = NotAShuttleLink;
11824  Utilities->CallLogPop(914);
11825  return(true);
11826  }
11827  Pos = Remainder.Pos(';'); // second segment delimiter
11828  if(Pos == 0) // no third segment so second must be a location, or cdt
11829  {
11830  Second = Remainder;
11831  if(Second == "cdt")
11832  {
11833  FormatType = TimeCmd;
11834  ShuttleLinkType = NotAShuttleLink;
11835  LocationType = AtLocation;
11836  SequenceType = IntermediateSequence;
11837  Utilities->CallLogPop(915);
11838  return(true);
11839  }
11840  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
11841  {
11842  Utilities->CallLogPop(916);
11843  return(false);
11844  }
11845  else
11846  {
11847  FormatType = TimeLoc;
11848  LocationType = AtLocation;
11849  SequenceType = IntermediateSequence;
11850  ShuttleLinkType = NotAShuttleLink;
11851  Utilities->CallLogPop(917);
11852  return(true);
11853  }
11854  }
11855  // here if second segment is a command, with a third & maybe fourth segments
11856  if((Pos != 4) && (Pos != 7) && (Pos != 8))
11857  {
11858  Utilities->CallLogPop(918);
11859  return(false);
11860  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
11861  }
11862  Second = Remainder.SubString(1, Pos - 1); // command
11863 
11864  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11865  // details
11866  Pos = Remainder.Pos(';'); // third segment delimiter
11867  if(Pos == 0)
11868  {
11869  Third = Remainder;
11870  }
11871  else
11872  {
11873  Third = Remainder.SubString(1, Pos - 1);
11874  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11875  }
11876 
11877  if((Second == "Snt") || (Second == "Snt-sh"))
11878  // third has to be 2 element idents with a space between
11879  {
11880  int SpacePos = Third.Pos(' ');
11881  if(SpacePos == 0)
11882  {
11883  Utilities->CallLogPop(919);
11884  return(false); // no space
11885  }
11886  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
11887  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
11888  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
11889  if(CheckLocationsExistInRailway)
11890  {
11891  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
11892  {
11893  Utilities->CallLogPop(920);
11894  return(false);
11895  }
11896  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
11897  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
11898  }
11899  if(Second == "Snt")
11900  {
11901  FormatType = StartNew;
11902  SequenceType = StartSequence;
11903  LocationType = NoLocation;
11904  // can't be set until know whether located or not - done in SecondPassActions
11905  ShuttleLinkType = NotAShuttleLink;
11906  }
11907  else // Snt-sh
11908  {
11909  FormatType = SNTShuttle;
11910  LocationType = AtLocation;
11911  SequenceType = StartSequence;
11912  ShuttleLinkType = ShuttleLink;
11913  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
11914  {
11915  Utilities->CallLogPop(1038);
11916  return(false);
11917  }
11918  }
11919  Utilities->CallLogPop(921);
11920  return(true);
11921  }
11922  if(Second == "Sns-sh") // third & fourth have to be headcodes
11923  {
11924  FormatType = SNSShuttle;
11925  LocationType = AtLocation;
11926  SequenceType = StartSequence;
11927  ShuttleLinkType = ShuttleLink;
11928  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
11929  {
11930  Utilities->CallLogPop(1039);
11931  return(false);
11932  }
11933  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
11934  {
11935  Utilities->CallLogPop(1040);
11936  return(false);
11937  }
11938  Utilities->CallLogPop(1041);
11939  return(true);
11940  }
11941  if(Second == "F-nshs")
11942  {
11943  FormatType = FNSNonRepeatToShuttle;
11944  LocationType = AtLocation;
11945  SequenceType = FinishSequence;
11946  ShuttleLinkType = ShuttleLink;
11947  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
11948  {
11949  Utilities->CallLogPop(1047);
11950  return(false);
11951  }
11952  Utilities->CallLogPop(1048);
11953  return(true);
11954  }
11955  if(Second == "Sns-fsh")
11956  {
11957  FormatType = SNSNonRepeatFromShuttle;
11958  LocationType = AtLocation;
11959  SequenceType = StartSequence;
11960  ShuttleLinkType = ShuttleLink;
11961  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
11962  {
11963  Utilities->CallLogPop(1098);
11964  return(false);
11965  }
11966  Utilities->CallLogPop(1099);
11967  return(true);
11968  }
11969  if(Second == "Fns-sh") // third & fourth have to be headcodes
11970  {
11971  FormatType = FSHNewService;
11972  LocationType = AtLocation;
11973  SequenceType = FinishSequence;
11974  ShuttleLinkType = ShuttleLink;
11975  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
11976  {
11977  Utilities->CallLogPop(1050);
11978  return(false);
11979  }
11980  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
11981  {
11982  Utilities->CallLogPop(1051);
11983  return(false);
11984  }
11985  Utilities->CallLogPop(1052);
11986  return(true);
11987  }
11988  // new segment for 'pas'
11989  if(Second == "pas") // third has to be a location
11990  {
11991  FormatType = PassTime;
11992  LocationType = EnRoute;
11993  SequenceType = IntermediateSequence;
11994  ShuttleLinkType = NotAShuttleLink;
11995  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
11996  {
11997  Utilities->CallLogPop(1515);
11998  return(false);
11999  }
12000  Utilities->CallLogPop(1516);
12001  return(true);
12002  }
12003  // new segment for revised 'Fer'
12004  if(Second == "Fer")
12005  // third has to be a set of IDs separated by spaces, and at least 1
12006  {
12007  FormatType = ExitRailway;
12008  LocationType = EnRoute;
12009  SequenceType = FinishSequence;
12010  ShuttleLinkType = NotAShuttleLink;
12011  if(CheckLocationsExistInRailway)
12012  {
12013  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
12014  {
12015  Utilities->CallLogPop(1519);
12016  return(false);
12017  }
12018  }
12019  Utilities->CallLogPop(1520);
12020  return(true);
12021  }
12022  if(Second == "dsc") //new at v2.15.0 - change description
12023  {
12024  if(Third.Length() > 60)
12025  {
12026  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters in '" + Third + "'");
12027  Utilities->CallLogPop(2582);
12028  return(false);
12029  }
12030  for(int x = 1; x < Third.Length() + 1; x++)
12031  {
12032 // if((Third[x] < ' ') || (Third[x] > '~')) changed at v2.16.0 to allow extended characters in location names
12033  if((Third[x] < ' ') && (Third[x] >= 0))
12034  {
12035  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + Third + "'");
12036  Utilities->CallLogPop(2583);
12037  return(false);
12038  }
12039  }
12040  FormatType = TimeCmdDescription;
12041  LocationType = AtLocation;
12042  SequenceType = IntermediateSequence;
12043  ShuttleLinkType = NotAShuttleLink;
12044  Utilities->CallLogPop(2604);
12045  return(true);
12046  }
12047 
12048 // if((Second == "fsp") || (Second == "fsp")) then there can optionlly be a fourth: xx-yy where xx = percentage mass & yy = percentage power in the split train
12049 
12050  // all remainder must be TimeCmdHeadCode types to be valid
12051  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
12052  (Second != "Frh-sh"))
12053  {
12054  Utilities->CallLogPop(922);
12055  return(false); // all TimeCmdHeadCode types
12056  }
12057  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
12058  {
12059  Utilities->CallLogPop(923);
12060  return(false);
12061  }
12062  FormatType = TimeCmdHeadCode;
12063  LocationType = AtLocation;
12064  if(Second == "Frh-sh")
12065  {
12066  ShuttleLinkType = ShuttleLink;
12067  }
12068  else
12069  {
12070  ShuttleLinkType = NotAShuttleLink;
12071  }
12072  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
12073  {
12074  SequenceType = FinishSequence;
12075  }
12076  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
12077  {
12078  SequenceType = IntermediateSequence;
12079  }
12080  if((Second == "Sfs") || (Second == "Sns"))
12081  {
12082  SequenceType = StartSequence;
12083  }
12084  //new at v2.15.0 to allow splits to have different characteristics - Fourth specifies: AA-BB where AA & BB can be 1 or 2 digits, AA is percentage mass
12085  if((Fourth != "") && ((Second == "fsp") || (Second == "rsp"))) //& BB is percentage power allocated to the split off train
12086  {
12087  if(!CheckFourthValidityForSplit(Fourth, GiveMessages))
12088  {
12089  Utilities->CallLogPop(2584);
12090  return(false);
12091  }
12092  }
12093  Utilities->CallLogPop(924);
12094  return(true);
12095 }
12096 
12097 // ---------------------------------------------------------------------------
12098 
12099 bool TTrainController::CheckFourthValidityForSplit(AnsiString SplitDistributionString, bool GiveMessages) //new at v2.15.0
12100 {
12101  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CheckFourthValidityForSplit," + SplitDistributionString);
12102  bool ErrorFlag = false;
12103  int x, y;
12104  if((SplitDistributionString.Length() > 6 ) || (SplitDistributionString.Length() < 3))
12105  {
12106  ErrorFlag = true;
12107  }
12108  int pos = SplitDistributionString.Pos('-');
12109  if(pos == 0)
12110  {
12111  ErrorFlag = true;
12112  }
12113  else
12114  {
12115  AnsiString MassStr = SplitDistributionString.SubString(1, pos - 1);
12116  AnsiString PowerStr = SplitDistributionString.SubString(pos + 1, SplitDistributionString.Length() - pos);
12117  try //allows for one or two digit percentages
12118  {
12119  int x = MassStr.ToInt();
12120  int y = PowerStr.ToInt();
12121  if((x > 99) || (x < 1) || (y > 100) || (y < 0))
12122  {
12123  ErrorFlag = true;
12124  }
12125  }
12126  catch(const Exception &e) //non-error catch
12127  {
12128  ErrorFlag = true;
12129  }
12130  }
12131  if(ErrorFlag)
12132  {
12133  TimetableMessage(GiveMessages, "Error in split distribution " + SplitDistributionString + ", should be 'AA-BB' where AA is the percentage mass (min 1, max 99) and BB the percentage " +
12134  "power for the new split-off train");
12135  Utilities->CallLogPop(2585);
12136  return(false);
12137  }
12138  Utilities->CallLogPop(2601);
12139  return(true);
12140 }
12141 
12142 // ---------------------------------------------------------------------------
12143 
12144 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
12145 {
12146  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with 'digit-digit-colon'
12147  // and contains no control characters changed at v2.16.0 to allow extended characters in location names
12148  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
12149  if(LocStr == "")
12150  {
12151  Utilities->CallLogPop(1353);
12152  return(false); // has to have at least one character
12153  }
12154 // if((LocStr[1] >= '0') && (LocStr[1] <= '9')) //changed at v2.16.0 to allow locations to begine with digits, if 'digit-digit-colon' then must be a time
12155  if((LocStr.Length() >= 3) && (LocStr[1] >= '0') && (LocStr[1] <= '9') && (LocStr[2] >= '0') && (LocStr[2] <= '9') && (LocStr[3] == ':'))
12156  {
12157  Utilities->CallLogPop(1354);
12158  return(false); // can't begin with 'digit-digit-colon' as this regarded as a time
12159  }
12160  for(int x = 1; x < LocStr.Length() + 1; x++)
12161  {
12162  if(((LocStr[x] < ' ') && (LocStr[x] >= 0)) || (LocStr[x] == ',') || (LocStr[x] == ';')) //changed at v2.16.0 to allow extended characters in location names
12163  {
12164  Utilities->CallLogPop(1355);
12165  return(false); // contains a special character or ',' or ';'
12166  }
12167 /*
12168  if(LocStr[x] > 'z') //dropped at v2.16.0 to allow extended characters in location names
12169  {
12170  Utilities->CallLogPop(1356);
12171  return(false); // contains a character outside the standard ASCII set
12172  }
12173 */
12174  }
12175  // check exists in railway location list if CheckLocationsExistInRailway is true
12176  if(CheckLocationsExistInRailway)
12177  {
12178  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
12179  {
12180  TimetableMessage(GiveMessages, "Location name '" + LocStr +
12181  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
12182  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
12183  "that includes a continuation will not be valid.");
12184  Utilities->CallLogPop(1357);
12185  return(false);
12186  }
12187  }
12188  Utilities->CallLogPop(1358);
12189  return(true);
12190 }
12191 
12192 // ---------------------------------------------------------------------------
12193 
12194 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
12195 {
12196  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
12197  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
12198  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
12199  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
12200  HeadCode);
12201  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
12202  {
12203  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
12204  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Sns, Snt-sh, Sns-sh, Fns, Fns-sh or Frh-sh");
12205  Utilities->CallLogPop(1359);
12206  return(false);
12207  }
12208  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
12209  for(int x = 1; x < (HeadCode.Length() + 1); x++)
12210  {
12211  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
12212  {
12213  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
12214  Utilities->CallLogPop(1895);
12215  return(false);
12216  }
12217  }
12218  // secondly ensure the true Headcode only has letters or digits
12219  for(int x = 3; x >= 0; x--)
12220  {
12221  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
12222  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
12223  {
12224  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
12225  Utilities->CallLogPop(1790);
12226  return(false);
12227  }
12228  }
12229  Utilities->CallLogPop(1364);
12230  return(true);
12231 }
12232 
12233 // ---------------------------------------------------------------------------
12234 
12235 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
12236 // set of track element IDs, separated by spaces, and at least 1 present
12237 {
12238  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndPopulateListOfIDs," + IDSet); //had wrong title, changed at v2.13.0
12239  ExitList.clear();
12240  AnsiString CurrentID = "";
12241 
12242  if(IDSet.Length() == 0)
12243  {
12244  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
12245  Utilities->CallLogPop(1521);
12246  return(false);
12247  }
12248  for(int x = 1; x <= IDSet.Length(); x++)
12249  {
12250  char C = IDSet[x];
12251  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
12252  {
12253  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
12254  Utilities->CallLogPop(1522);
12255  return(false);
12256  }
12257 /* don't use, error checks in GetTrackVectorPositionFromString instead
12258  if(C == '-') //this section added at v2.13.0 because of Amon Sadler's error file submitted 24/03/22
12259  {
12260  if((x==1) || (x == IDSet.Length()))
12261  {
12262  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
12263  Utilities->CallLogPop(2479);
12264  return(false);
12265  }
12266  if((IDSet[x-1] < '0') || (IDSet[x-1] > '9') || (IDSet[x+1] < '0') || (IDSet[x+1] > '9'))
12267  {
12268  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
12269  Utilities->CallLogPop(2480);
12270  return(false);
12271  }
12272  }
12273 */
12274  }
12275  int Pos = IDSet.Pos(' '); // look for the first space
12276 
12277  while(true)
12278  {
12279  if(Pos == 0)
12280  {
12281  CurrentID = IDSet;
12282  IDSet = "";
12283  }
12284  else
12285  {
12286  CurrentID = IDSet.SubString(1, Pos - 1);
12287  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
12288  }
12289  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
12290  if(VecPos == -1)
12291  {
12292  Utilities->CallLogPop(1523);
12293  return(false); // messages given in GetTrackVectorPositionFromString
12294  }
12295  else
12296  {
12297  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
12298  {
12299  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
12300  Utilities->CallLogPop(1524);
12301  return(false);
12302  }
12303  else
12304  {
12305  // first check for duplicates
12306  if(!ExitList.empty())
12307  {
12308  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
12309  {
12310  if(*ELIT == VecPos)
12311  {
12312  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
12313  Utilities->CallLogPop(1532);
12314  return(false);
12315  }
12316  }
12317  }
12318  // of OK add it to the list
12319  ExitList.push_back(VecPos);
12320  }
12321  }
12322  if(IDSet == "")
12323  {
12324  Utilities->CallLogPop(1525);
12325  return(true);
12326  }
12327  else
12328  {
12329  Pos = IDSet.Pos(' '); // look for the next space
12330  }
12331  } // while(true)
12332 }
12333 
12334 // ---------------------------------------------------------------------------
12335 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
12336  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
12337 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
12338 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
12339 // of each item
12340 {
12341  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
12342  int Pos = 0;
12343  AnsiString Remainder = "";
12344  int SemiColonCount = 0;
12345 
12346  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
12347  {
12348  if(TrainInfoStr[x] == ';')
12349  {
12350  SemiColonCount++;
12351  }
12352  }
12353  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
12354  {
12355  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
12356  "'. Should be headcode + optional description for a continuing service;" +
12357  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
12358  Utilities->CallLogPop(880);
12359  return(false);
12360  }
12361  if(SemiColonCount == 0)
12362  {
12363  HeadCode = TrainInfoStr;
12364  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
12365  {
12366  Utilities->CallLogPop(881);
12367  return(false);
12368  }
12369  Utilities->CallLogPop(882);
12370  return(true);
12371  }
12372  if(SemiColonCount == 1) // headcode & description only
12373  {
12374  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
12375  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
12376  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
12377  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
12378  {
12379  Utilities->CallLogPop(883);
12380  return(false);
12381  }
12382  if(Description == "")
12383  {
12384  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
12385  Utilities->CallLogPop(884);
12386  return(false);
12387  }
12388  if(Description.Length() > 60)
12389  {
12390  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
12391  Utilities->CallLogPop(1157);
12392  return(false);
12393  }
12394  for(int x = 1; x < Description.Length() + 1; x++)
12395  {
12396 // if((Description[x] < ' ') || (Description[x] > '~')) changed at v2.16.0 to allow extended characters in location names
12397  if((Description[x] < ' ') && (Description[x] >= 0))
12398  {
12399  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
12400  Utilities->CallLogPop(885);
12401  return(false);
12402  }
12403  }
12404  Utilities->CallLogPop(886);
12405  return(true);
12406  }
12407  // if here must have 6 or 7 semicolons
12408  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
12409  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
12410  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
12411  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
12412  {
12413  Utilities->CallLogPop(887);
12414  return(false);
12415  }
12416  Pos = Remainder.Pos(';'); // 2nd delimiter
12417  Description = Remainder.SubString(1, Pos - 1);
12418  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12419  if(Description == "")
12420  {
12421  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
12422  Utilities->CallLogPop(888);
12423  return(false);
12424  }
12425  if(Description.Length() > 60)
12426  {
12427  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
12428  Utilities->CallLogPop(1158);
12429  return(false);
12430  }
12431  for(int x = 1; x < Description.Length() + 1; x++)
12432  {
12433 // if((Description[x] < ' ') || (Description[x] > 126)) changed at v2.16.0 to allow extended characters in location names
12434  if((Description[x] < ' ') && (Description[x] >= 0))
12435  {
12436  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
12437  Utilities->CallLogPop(889);
12438  return(false);
12439  }
12440  }
12441  Pos = Remainder.Pos(';'); // 3rd delimiter
12442  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
12443 
12444  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12445  if(StartSpeedStr == "")
12446  {
12447  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
12448  Utilities->CallLogPop(890);
12449  return(false);
12450  }
12451  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
12452  {
12453  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
12454  {
12455  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
12456  Utilities->CallLogPop(891);
12457  return(false);
12458  }
12459  }
12460  StartSpeed = StartSpeedStr.ToInt();
12461  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
12462  {
12463  StartSpeed = TTrain::MaximumSpeedLimit;
12464  if(!SSHigh) // added at v2.4.0
12465  {
12466  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12467  SSHigh = true;
12468  }
12469  }
12470  Pos = Remainder.Pos(';'); // 4th delimiter
12471  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
12472 
12473  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12474  if(MaxRunningSpeedStr == "")
12475  {
12476  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
12477  Utilities->CallLogPop(892);
12478  return(false);
12479  }
12480  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
12481  {
12482  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
12483  {
12484  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
12485  Utilities->CallLogPop(893);
12486  return(false);
12487  }
12488  }
12489  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
12490  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
12491  {
12492  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
12493  if(!MRSHigh) // added at v2.4.0
12494  {
12495  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12496  MRSHigh = true;
12497  }
12498  }
12499  if(MaxRunningSpeed < 10)
12500  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
12501  {
12502  MaxRunningSpeed = 10;
12503  if(!MRSLow) // added at v2.4.0
12504  {
12505  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
12506  MRSLow = true;
12507  }
12508  }
12509  Pos = Remainder.Pos(';'); // 5th delimiter
12510  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
12511 
12512  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12513  if(MassStr == "")
12514  {
12515  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
12516  Utilities->CallLogPop(895);
12517  return(false);
12518  }
12519  for(int x = 1; x < MassStr.Length() + 1; x++)
12520  {
12521  if((MassStr[x] < '0') || (MassStr[x] > '9'))
12522  {
12523  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
12524  Utilities->CallLogPop(896);
12525  return(false);
12526  }
12527  }
12528  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
12529  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
12530  {
12531  Mass = TTrain::MaximumMassLimit;
12532  if(!MassHigh) // added at v2.4.0
12533  {
12534  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
12535  MassHigh = true;
12536  }
12537  }
12538  if(Mass == 0)
12539  {
12540  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
12541  Utilities->CallLogPop(897);
12542  return(false);
12543  }
12544  Pos = Remainder.Pos(';'); // 6th delimiter
12545  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
12546 
12547  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12548  if(MaxBrakeForceStr == "")
12549  {
12550  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
12551  Utilities->CallLogPop(898);
12552  return(false);
12553  }
12554  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
12555  {
12556  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
12557  {
12558  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
12559  Utilities->CallLogPop(899);
12560  return(false);
12561  }
12562  }
12563  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
12564 
12565  // convert to kg force
12566  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
12567  {
12568  MaxBrakeForce = Mass;
12569  if(!BFHigh) // added at v2.4.0
12570  {
12571  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
12572  BFHigh = true;
12573  }
12574  }
12575  if((MaxBrakeForce / Mass) < 0.01)
12576  {
12577  MaxBrakeForce = Mass * 0.01;
12578  if(!BFLow) // added at v2.4.0
12579  {
12580  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
12581  BFLow = true;
12582  }
12583  }
12584  // convert to m/s/s
12585  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
12586  // now may have just a power entry or power and signaller max. speed
12587  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
12588 
12589  if(SemiColonCount == 6)
12590  {
12591  GrossPowerStr = Remainder;
12592  SignallerSpeedStr = "30"; // default value
12593  }
12594  else // must be 7
12595  {
12596  Pos = Remainder.Pos(';'); // 7th delimiter
12597  GrossPowerStr = Remainder.SubString(1, Pos - 1);
12598  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12599  }
12600  // deal with GrossPower
12601  if(GrossPowerStr == "")
12602  {
12603  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
12604  Utilities->CallLogPop(901);
12605  return(false);
12606  }
12607  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
12608  {
12609  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
12610  {
12611  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
12612  Utilities->CallLogPop(902);
12613  return(false);
12614  }
12615  }
12616 
12617  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
12618 
12619  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
12620  {
12621  GrossPower = TTrain::MaximumPowerLimit;
12622  if(!PwrHigh)
12623  {
12624  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
12625  PwrHigh = true;
12626  }
12627  }
12628  else if(GrossPower == 0) // changed at v2.4.0
12629  {
12630  GrossPower = 0.1;
12631  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
12632  }
12633  else if((GrossPower > 0) && (GrossPower < 10000))
12634  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
12635  {
12636  GrossPower = 10000;
12637  }
12638  PowerAtRail = GrossPower * 0.8;
12639  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
12640 
12641  // deal with SignallerSpeed
12642  if(SignallerSpeedStr == "")
12643  {
12644  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
12645  Utilities->CallLogPop(1771);
12646  return(false);
12647  }
12648  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
12649  {
12650  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
12651  {
12652  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
12653  Utilities->CallLogPop(1769);
12654  return(false);
12655  }
12656  }
12657  SignallerSpeed = SignallerSpeedStr.ToInt();
12658  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
12659  {
12660  SignallerSpeed = TTrain::MaximumSpeedLimit;
12661  if(!SigSHigh)
12662  {
12663  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12664  SigSHigh = true;
12665  }
12666  }
12667  if(SignallerSpeed < 10)
12668  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
12669  {
12670  SignallerSpeed = 10;
12671  if(!SigSLow)
12672  {
12673  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
12674  SigSLow = true;
12675  }
12676  // Utilities->CallLogPop(1770);
12677  // return false;
12678  }
12679  Utilities->CallLogPop(904);
12680  return(true);
12681 }
12682 
12683 // ---------------------------------------------------------------------------
12684 
12685 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
12686  bool GiveMessages)
12687 {
12688  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
12689  // function checks validity of each item and returns false for error
12690  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
12691  if(OneEntry.Length() < 7)
12692  {
12693  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12694  Utilities->CallLogPop(865);
12695  return(false);
12696  }
12697  int SemiColonCount = 0;
12698 
12699  for(int x = 1; x < OneEntry.Length() + 1; x++)
12700  {
12701  if(OneEntry[x] == ';')
12702  {
12703  SemiColonCount++;
12704  }
12705  }
12706  if(SemiColonCount != 3)
12707  {
12708  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12709  Utilities->CallLogPop(866);
12710  return(false);
12711  }
12712  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
12713  {
12714  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12715  Utilities->CallLogPop(867);
12716  return(false);
12717  }
12718  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
12719  // strip off R;
12720 
12721  int Pos = 0;
12722 
12723  Pos = Remainder.Pos(';');
12724  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
12725 
12726  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12727  if(MinutesStr == "")
12728  {
12729  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
12730  Utilities->CallLogPop(868);
12731  return(false);
12732  }
12733  if(MinutesStr.Length() > 3)
12734  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
12735  {
12736  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
12737  Utilities->CallLogPop(2119);
12738  return(false);
12739  }
12740  for(int x = 1; x < MinutesStr.Length() + 1; x++)
12741  {
12742  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
12743  {
12744  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
12745  Utilities->CallLogPop(869);
12746  return(false);
12747  }
12748  }
12749  RearStartOrRepeatMins = MinutesStr.ToInt();
12750  if(RearStartOrRepeatMins == 0)
12751  {
12752  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
12753  Utilities->CallLogPop(870);
12754  return(false);
12755  }
12756  Pos = Remainder.Pos(';');
12757  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
12758 
12759  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12760  if(DigitsStr == "")
12761  {
12762  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
12763  Utilities->CallLogPop(871);
12764  return(false);
12765  }
12766  for(int x = 1; x < DigitsStr.Length() + 1; x++)
12767  {
12768  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
12769  {
12770  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
12771  Utilities->CallLogPop(872);
12772  return(false);
12773  }
12774  }
12775  if(DigitsStr.Length() > 2)
12776  {
12777  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
12778  Utilities->CallLogPop(873);
12779  return(false);
12780  }
12781  FrontStartOrRepeatDigits = DigitsStr.ToInt();
12782 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
12783  route rather than the service
12784  if(FrontStartOrRepeatDigits == 0)
12785  {
12786  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
12787  Utilities->CallLogPop(874);
12788  return false;
12789  }
12790 */
12791  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
12792  // new for v0.6b for unrestricted headcodes
12793  {
12794  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
12795  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
12796  Utilities->CallLogPop(1889);
12797  return(false);
12798  }
12799  AnsiString NumberStr = Remainder;
12800 
12801  if(NumberStr == "")
12802  {
12803  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
12804  Utilities->CallLogPop(875);
12805  return(false);
12806  }
12807  if(NumberStr.Length() > 4)
12808  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
12809  {
12810  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
12811  Utilities->CallLogPop(2118);
12812  return(false);
12813  }
12814  for(int x = 1; x < NumberStr.Length() + 1; x++)
12815  {
12816  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
12817  // catches negative numbers
12818  {
12819  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
12820  Utilities->CallLogPop(876);
12821  return(false);
12822  }
12823  }
12824  NumberOfRepeats = NumberStr.ToInt();
12825  if(NumberOfRepeats == 0)
12826  {
12827  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
12828  Utilities->CallLogPop(877);
12829  return(false);
12830  }
12831  Utilities->CallLogPop(878);
12832  return(true);
12833 }
12834 
12835 // ---------------------------------------------------------------------------
12836 
12837 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag) //TwoLocationFlag added at v2.9.1
12838 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
12839  vector rather than the timetable
12840  Note also that for unlocated Snt entries the LocationType hasn't yet been set
12841 
12842  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
12843 
12844  For info:-
12845  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
12846  {
12847  public:
12848  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled event entries, null
12850  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
12851  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
12852  int NumberOfRepeats; ///< the number of repeating services
12853  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
12855  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
12857  TNumList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
12858  TTimetableFormatType FormatType; ///< defines the timetable action type
12859  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
12860  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
12861  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
12862  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
12864  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
12866 
12867  // inline function
12868 
12870  TActionVectorEntry() {
12871  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
12872  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
12873  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
12874  Warning = false; SignallerControl = false;
12875  }
12876  };
12877 
12878  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
12879 
12880  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
12881  {
12882  public:
12883  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
12886  double MaxBrakeRate; ///< in metres/sec/sec
12887  double MaxRunningSpeed; ///< in km/h
12888  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
12889  int Mass; ///< in kg
12890  int NumberOfTrains; ///< number of repeats + 1
12891  int SignallerSpeed; ///< in km/h for use when under signaller control
12892  int StartSpeed; ///< in km/h
12893  TActionVector ActionVector; ///< all the actions for the train
12894  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
12895 
12896  //inline function
12897 
12899  TTrainDataEntry()
12900  {
12901  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
12902  }
12903  };
12904 
12905  Allowable successors:-
12906  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
12907  Snt located -> No starts, no finishes except Frh, Fjo (as of v2.0.0), Fns, and F-nshs, no repeat, pas, TimeTimeLoc or TimeLoc arr; any other cmd or TimeLoc (dep) OK
12908  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
12909  Sfs -> No starts, finishes except Frh & Fjo (as of v2.15.0), repeats, pas, TimeTimeLoc, TimeLoc arr, rsp, fsp; any other cmd or TimeLoc (dep) OK [
12910  must have departure & arrival before another split]
12911  Sns -> No starts, finishes except Frh & Fjo (as of v2.15.0), repeats, pas, TimeTimeLoc or TimeLoc arr; any other cmd or TimeLoc (dep) OK
12912  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12913  set location, else fails)
12914  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12915  set location, else fails)
12916  Fns -> R only [must be preceded by a TimeLoc arrival at the finish location, not necessarily immediately]
12917  F-nshs -> Nothing (no repeats permitted)
12918  Fjo -> R only
12919  Frh -> R only
12920  Fer -> R only
12921  Frh-sh -> R only
12922  Fns-sh -> R only
12923  jbo -> No starts, repeats, pas, Fer or TimeTimeLoc; TimeLoc (dep), others OK [must be preceded by an event whose location is set]
12924  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
12925  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
12926  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
12927  dsc -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
12928  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12929  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12930  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12931  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12932  Repeat -> Nothing
12933 
12934  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
12935  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
12936  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
12937  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
12938  Check all times increase or stay same through ActionVector
12939  Cycle through all entries in vector setting arr & dep times based on above list
12940  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
12941  Check locations match the arr & dep TimeLoc entries
12942  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
12943  Make above valid succession checks
12944  Check all splits have matching Sfs headcodes (both ways), add locations to Sfs's & check times same
12945  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to Sns's & check times same
12946  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
12947  Check each Fns has matching Sns headcodes (both ways), add locations to Fns's & check times same
12948  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
12949  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
12950  Set train info for Sfs & Sns entries
12951  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
12952  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
12953  element at each end, or length of 3 & 1 extra element at either end
12954  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
12955  Check all Cmds have EventTime set & Arr & Dep times = -1
12956  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
12957 
12958  Give messages in function if errors detected and clear the vector. Return false for failure.
12959 */
12960 
12961 /* Earlier checks:-
12962  Checks carried out with error messages in this function:-
12963  At least one comma in the line (it's based on a csv file);
12964  No entries following train information;
12965  At least one comma in remainder after train information (i.e at least a start and a finish entry);
12966  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
12967  First entry not a start entry;
12968  Train information incomplete before a start entry;
12969  Entry follows a finish entry but doesn't begin with 'R';
12970  SplitEntry returns false in a finish entry - message repeats the entry for information;
12971  Last action entry isn't a finish entry.
12972 
12973  Function returns false with no message if:-
12974  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
12975  time is found at all then an error message is given in the calling function);
12976  SplitTrainInfo returns false (message given in called function);
12977  SplitRepeat returns false (message given in called function).
12978 
12979 Double crosslink (shuttle) table: [OtherHeadCode, NonRepeatingShuttleLinkHeadCode, LinkedTrainEntryPtr, NonRepeatingShuttleLinkEntryPtr] <-- these for easier searching for this table
12980 
12981 Command Format OtherHead NonRepeating- LinkedTrain- NonRepeating- Decsription
12982  Code ShuttleLink- EntryPtr ShuttleLink
12983  HeadCode EntryPtr
12984 
12985 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
12986 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
12987 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
12988 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (fdr-shld be rtn)N (shld be fdr) Luckily NonRep link not needed
12989 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
12990 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
12991 
12992 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
12993 
12994 */
12995 {
12996  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
12997  if(TrainDataVector.empty())
12998  {
12999  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
13000  TrainDataVector.clear();
13001  Utilities->CallLogPop(1832);
13002  return(false);
13003  }
13004 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
13005  1) must have at least one actionvector entry
13006  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
13007  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
13008  4) first entry must be a start;
13009  4a) if first entry is Snt and second is a finish then it can't be Fns-sh or Frh-sh
13010  4b) if first entry is Sns or Sfs and second is a finish then it must be either Frh or Fjo
13011  4c) if first entry is Snt-sh, Sns-sh or Sns-fsh second can't be a finish
13012  5) a start must be the first entry;
13013  6) a repeat entry must be the last;
13014  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
13015  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
13016  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
13017 */
13018 
13019  TwoLocationList.clear(); //empty the list to begin with, added at v2.9.1
13020  TwoLocationFlag = false; //added at v2.9.1
13021  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
13022  {
13023  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13024  if(TrainDataVector.at(x).ActionVector.empty())
13025  {
13026  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
13027  TrainDataVector.clear();
13028  Utilities->CallLogPop(1833);
13029  return(false);
13030  }
13031  }
13032  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
13033  {
13034  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13035  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13036  if(!(AVEntry0.SignallerControl))
13037  {
13038  if(TrainDataVector.at(x).ActionVector.size() == 1)
13039  {
13040  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
13041  TrainDataVector.clear();
13042  Utilities->CallLogPop(1822);
13043  return(false);
13044  }
13045  }
13046  }
13047  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
13048  {
13049  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13050  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13051  if(AVEntry0.SignallerControl)
13052  {
13053  if(TrainDataVector.at(x).ActionVector.size() > 2)
13054  {
13055  SecondPassMessage(GiveMessages,
13056  "Error in timetable - a signaller controlled service can have no more than one item (a repeat) after the start event, see: " +
13057  TDEntry.HeadCode);
13058  TrainDataVector.clear();
13059  Utilities->CallLogPop(1837);
13060  return(false);
13061  }
13062  if(TrainDataVector.at(x).ActionVector.size() > 1)
13063  {
13064  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13065  if(AVEntry1.FormatType != Repeat)
13066  {
13067  SecondPassMessage(GiveMessages,
13068  "Error in timetable - a signaller controlled service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
13069  TrainDataVector.clear();
13070  Utilities->CallLogPop(1838);
13071  return(false);
13072  }
13073  }
13074  }
13075  }
13076  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
13077  {
13078  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13079  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13080  if(AVEntry0.SequenceType != StartSequence)
13081  {
13082  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
13083  TrainDataVector.clear();
13084  Utilities->CallLogPop(1824);
13085  return(false);
13086  }
13087  if((AVEntry0.Command == "Snt") && !AVEntry0.SignallerControl) // (4a) sig control condition added so there is a second AVEntry
13088  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
13089  // and others for a located Snt, but those checks done later
13090  {
13091  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1); // must be a second entry if first not signallercontrol
13092  if((AVEntry1.SequenceType == FinishSequence) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
13093  {
13094  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
13095  TDEntry.HeadCode); //these are the only AtLoc finishes not allowed
13096  TrainDataVector.clear();
13097  Utilities->CallLogPop(2046);
13098  return(false);
13099  }
13100  }
13101  if((AVEntry0.Command == "Sns") || (AVEntry0.Command == "Sfs")) // (4b)
13102  // 4b added at v2.15.0
13103  {
13104  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13105  if((AVEntry1.SequenceType == FinishSequence) && (AVEntry1.Command != "Frh") && (AVEntry1.Command != "Fjo"))
13106  {
13107  SecondPassMessage(GiveMessages, "Error in timetable - only 'Frh' or 'Fjo' finish events are permitted immediately after an 'Sns' or 'Sfs' event for: " +
13108  TDEntry.HeadCode);
13109  TrainDataVector.clear();
13110  Utilities->CallLogPop(2580);
13111  return(false);
13112  }
13113  }
13114  if((AVEntry0.Command == "Snt-sh") || (AVEntry0.Command == "Sns-sh") || (AVEntry0.Command == "Sns-fsh")) // (4c)
13115  // 4c added at v2.15.0
13116  {
13117  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13118  if(AVEntry1.SequenceType == FinishSequence)
13119  {
13120  SecondPassMessage(GiveMessages, "Error in timetable - a finish event can't immediately follow an 'Snt-sh', 'Sns-sh' or 'Sns-fsh' event for: " + TDEntry.HeadCode);
13121  TrainDataVector.clear();
13122  Utilities->CallLogPop(2616);
13123  return(false);
13124  }
13125  }
13126  }
13127  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
13128  {
13129  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13130  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13131  {
13132  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13133  if((AVEntry.SequenceType == StartSequence) && (y != 0))
13134  {
13135  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
13136  TrainDataVector.clear();
13137  Utilities->CallLogPop(1825);
13138  return(false);
13139  }
13140  }
13141  }
13142  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
13143  {
13144  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13145  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13146  {
13147  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13148  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
13149  {
13150  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
13151  TrainDataVector.clear();
13152  Utilities->CallLogPop(1826);
13153  return(false);
13154  }
13155  }
13156  }
13157  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
13158  {
13159  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13160  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13161  {
13162  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13163  if((y == 0) && AVEntry.SignallerControl)
13164  {
13165  break;
13166  }
13167  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
13168  {
13169  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != FinishSequence))
13170  {
13171  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
13172  TrainDataVector.clear();
13173  Utilities->CallLogPop(1827);
13174  return(false);
13175  }
13176  if(AVEntry.FormatType == Repeat)
13177  {
13178  const TActionVectorEntry &LastButOneAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
13179  if(LastButOneAVEntry.SequenceType != FinishSequence)
13180  {
13181  SecondPassMessage(GiveMessages, "Error in timetable - the event immediately before the repeat must be a finish for: " + TDEntry.HeadCode);
13182  TrainDataVector.clear();
13183  Utilities->CallLogPop(1828);
13184  return(false);
13185  }
13186  }
13187  }
13188  }
13189  }
13190  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
13191  {
13192  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13193  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13194  {
13195  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13196  if(AVEntry.SequenceType == FinishSequence)
13197  {
13198  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
13199  {
13200  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
13201  TrainDataVector.clear();
13202  Utilities->CallLogPop(1829);
13203  return(false);
13204  }
13205  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
13206  {
13207  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
13208  {
13209  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " + TDEntry.HeadCode);
13210  TrainDataVector.clear();
13211  Utilities->CallLogPop(1830);
13212  return(false);
13213  }
13214  }
13215  }
13216  }
13217  }
13218 
13219  // end of new preliminary checks
13220 
13221  // check start event successor validity
13222  // For Snt & Snt-sh set location if stopped, don't set any times yet
13223  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13224  {
13225  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13226  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13227  // use reference so can change internals where necessary
13228  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
13229  {
13230  AnsiString LocationName = "";
13231  if(IsSNTEntryLocated(0, TDEntry, LocationName))
13232  // it is at a location
13233  {
13234  AVEntry0.LocationName = LocationName; //located Snt location name set
13235  AVEntry0.LocationType = AtLocation;
13236  // check successor validity for located Snt that isn't a SignallerControl entry
13237  if(!AVEntry0.SignallerControl)
13238  {
13239  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13240  // at least 2 entries present checked in integrity check so (1) valid
13241  if(!AtLocSuccessor(AVEntry1))
13242  {
13243  // Frh following Snt-sh will return false in location check, so no need to check here
13244  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
13245  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
13246  TrainDataVector.clear();
13247  Utilities->CallLogPop(523);
13248  return(false);
13249  }
13250  }
13251  }
13252  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
13253  {
13254  if(AVEntry0.Command == "Snt-sh")
13255  {
13256  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
13257  TrainDataVector.clear();
13258  Utilities->CallLogPop(1042);
13259  return(false);
13260  }
13261  AVEntry0.LocationType = EnRoute;
13262  if(!AVEntry0.SignallerControl)
13263  {
13264  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13265  // at least 2 entries checked in integrity check so (1) valid
13266  if(!MovingSuccessor(AVEntry1))
13267  {
13268  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
13269  TDEntry.HeadCode);
13270  TrainDataVector.clear();
13271  Utilities->CallLogPop(790);
13272  return(false);
13273  }
13274  }
13275  }
13276  }
13277  // check other start successors, all AtLoc
13278  else if(AVEntry0.SequenceType == StartSequence)
13279  {
13280  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13281  // at least 2 entries present checked in integrity check so (1) valid
13282  if(!AtLocSuccessor(AVEntry1))
13283  {
13284  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh', 'Snt-fsh' or 'Sns-fsh' followed by an illegal event for: " +
13285  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
13286  TrainDataVector.clear();
13287  Utilities->CallLogPop(793);
13288  return(false);
13289  }
13290  }
13291  }
13292 
13293 
13294  // set Sns-sh & Sns-fsh locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
13295  for(unsigned int x = 0; x < TrainDataVector.size(); x++) //at v2.15.0 set Sfs & Sns locations from corresponding fsp/rsp & Fns entries, shuttles ok as they are
13296  {
13297  bool FoundFlag = false;
13298  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13299  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13300  // use reference so can change internals
13301  if((AVEntry0.Command == "Sns-sh") || (AVEntry0.Command == "Sns-fsh"))
13302  {
13303  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
13304  {
13305  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
13306  if(AVEntry2.FormatType == TimeLoc)
13307  {
13308  FoundFlag = true;
13309  AVEntry0.LocationName = AVEntry2.LocationName; //Sns-sh & Sns-fsh location names set
13310  break;
13311  }
13312  }
13313  if(!FoundFlag)
13314  {
13315  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sns-sh' or 'Sns-fsh' event for: " + TDEntry.HeadCode);
13316  TrainDataVector.clear();
13317  Utilities->CallLogPop(851);
13318  return(false);
13319  }
13320  }
13321  }
13322 
13323 //carry out preliminary check on service ref linkages without setting any data - added at v2.15.0 as can be location errors if linked trains not present
13324 //first check for duplicates then linkages (also checked later but leave that in)
13325  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13326  {
13327  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13328  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13329  {
13330  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13331  if(AVEntry.OtherHeadCode != "")
13332  {
13333  if(!CheckForDuplicateCrossReferences(2, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
13334  {
13335  Utilities->CallLogPop(2610);
13336  return(false); // error message given in called function
13337  }
13338  }
13339  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13340  {
13341  if(!CheckForDuplicateCrossReferences(3, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
13342  {
13343  Utilities->CallLogPop(2611);
13344  return(false); // error message given in called function
13345  }
13346  }
13347  }
13348  }
13349 //cross reference check
13350  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13351  {
13352  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13353  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13354  {
13355  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13356  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13357  {
13358  if(AVEntry.OtherHeadCode != "")
13359  {
13360  if(!CheckCrossReferencesAndSetData(2, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, false, GiveMessages))
13361  // false = non-shuttle
13362  {
13363  Utilities->CallLogPop(2612);
13364  return(false); // error message given in called function
13365  }
13366  }
13367  }
13368  }
13369  }
13370 
13371 // now repeat the check just for the shuttles
13372  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13373  {
13374  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13375  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13376  {
13377  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13378  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
13379  {
13380  if(AVEntry.OtherHeadCode != "")
13381  {
13382  if(!CheckCrossReferencesAndSetData(3, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, false, GiveMessages))
13383  // true = shuttle
13384  {
13385  Utilities->CallLogPop(2613);
13386  return(false); // error message given in called function
13387  }
13388  }
13389  }
13390  }
13391  }
13392 
13393 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
13394  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13395  {
13396  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13397  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13398  {
13399  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13400  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13401  {
13402  if(!CheckNonRepeatingShuttleLinksAndSetData(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, false, GiveMessages))
13403  {
13404  Utilities->CallLogPop(2614);
13405  return(false); // error message given in called function
13406  }
13407  }
13408  }
13409  }
13410 
13411 
13412 //at v2.15.0 we want to set Sns, Sfs location names, but to set Sns & Sfs first need the linked Fns & fsp/rsp to have locations set as they aren't yet,
13413 //and before v2.15.0 they were set from the corresponding Sns & Sfs locations, which in turn were set from later TimeLoc departures. At v2.15.0 it is required to have
13414 //these commands followed by Frh & Fjo, so this is why we need the linked Fns & fsp/rsp to have locations set first. Now all Fns will have a TimeLoc before, so
13415 //that can provide its location, but fsp/rsp? Must they have a TimeLoc before? No, and can't rely on starting Sfs having the location set yet.
13416 //So, new restriction, insist on an rsp/fsp having a TimeLoc before it or a located Snt, and use one of those to set the location for the rsp/fsp and hence the linked Sfs.
13417 
13418 //NB can't allow an Sfs to be followed by another split or won't find a name, test with many existing tts then add an error to find it
13419 //Fns must be preceded by an arrival
13420 
13421 //set name for Fns from earlier location name or fail if can't find
13422  bool LocFoundFlag, FnsFoundFlag;
13423  TActionVectorEntry *AVEntryFns;
13424  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13425  {
13426  LocFoundFlag = false;
13427  FnsFoundFlag = false;
13428  for(int y = TrainDataVector.at(x).ActionVector.size() - 1; y >= 0; y--)
13429  {
13430  if(TrainDataVector.at(x).ActionVector.at(y).Command == "Fns")
13431  {
13432  AVEntryFns = &TrainDataVector.at(x).ActionVector.at(y);
13433  FnsFoundFlag = true;
13434  continue;
13435  }
13436  if(!FnsFoundFlag)
13437  {
13438  continue;
13439  }
13440  if(TrainDataVector.at(x).ActionVector.at(y).LocationName != "")
13441  {
13442  LocFoundFlag = true;
13443  AVEntryFns->LocationName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
13444 // double EVT = double(AVEntryFns->EventTime); //test
13445  break; //name found
13446  }
13447  if(TrainDataVector.at(x).ActionVector.at(y).LocationType == AtLocation) //not named yet
13448  {
13449  continue;
13450  }
13451  else
13452  {
13453  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' finish must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13454  TrainDataVector.clear();
13455  Utilities->CallLogPop(2596);
13456  return(false);
13457  }
13458  }
13459  if(FnsFoundFlag && !LocFoundFlag)
13460  {
13461  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' finish must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13462  TrainDataVector.clear();
13463  Utilities->CallLogPop(2597);
13464  return(false);
13465  }
13466  }
13467 
13468 //now set all names for Sns entries from the above, new at v2.15.0
13469  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13470  {
13471  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13472  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13473  // use reference so can change internals
13474  if(AVEntry0.Command == "Sns")
13475  {
13476  //new at v2.15.0. Only set location if have a forward and backward linkage at same time (these all set when SecondPassActions called). This isn't
13477  //rigorous as may have more than one, but if do then will be caught below in CheckCrossReferencesAndSetData
13478  //If fail to find location just ignore as will be caught later in CheckCrossReferencesAndSetData
13479  //note that at this stage the OtherHeadCode values are service refs, as haven't yet been changed back to headcodes until StripExcessFromHeadCode called
13480  //at end of this function
13481  //need to be the same: forward & reverse service refs, event times, commands correspond
13482 
13483  //successor checks first: /no starts, /finishes except Frh & Fjo (as of v2.15.0), /repeats, /pas, /TimeTimeLoc or /TimeLoc arr; any other cmd or TimeLoc (dep) OK
13484  if(TDEntry.ActionVector.size() < 2)
13485  {
13486  SecondPassMessage(GiveMessages, "Error in timetable - insufficient actions follwing an 'Sns' event for: " + TDEntry.HeadCode);
13487  TrainDataVector.clear();
13488  Utilities->CallLogPop(2598);
13489  return(false);
13490  }
13491  TActionVectorEntry AVEntry1 = TDEntry.ActionVector.at(1);
13492  if(!AtLocSuccessor(AVEntry1))
13493  {
13494  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sns' event is followed by an illegal event for: " + TDEntry.HeadCode +
13495  ". The event isn't valid for a stationary train.");
13496  TrainDataVector.clear();
13497  Utilities->CallLogPop(2599);
13498  return(false);
13499  }
13500  if((AVEntry1.SequenceType == StartSequence) || ((AVEntry1.SequenceType == FinishSequence) && (AVEntry1.Command != "Frh") && (AVEntry1.Command != "Fjo")) ||
13501  (AVEntry1.FormatType == Repeat))
13502  {
13503  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sns' event is followed by an illegal event for: " + TDEntry.HeadCode);
13504  TrainDataVector.clear();
13505  Utilities->CallLogPop(2600);
13506  return(false);
13507  }
13508 
13509  //now set the location and location type
13510  TDateTime SnsEventTime = AVEntry0.EventTime;
13511 // double EVT = double(SnsEventTime); //test
13512  AnsiString SnsServiceRef = TDEntry.ServiceReference;
13513  bool BreakFlag = false;
13514  for(unsigned int y = 0; y < TrainDataVector.size(); y++)
13515  {
13516  for(unsigned int z = 0; z < TrainDataVector.at(y).ActionVector.size(); z++)
13517  {
13518  if((TrainDataVector.at(y).ActionVector.at(z).Command == "Fns") && (SnsEventTime == TrainDataVector.at(y).ActionVector.at(z).EventTime) &&
13519  (TrainDataVector.at(y).HeadCode == AVEntry0.OtherHeadCode))
13520  { //forward linkage found
13521  if(TrainDataVector.at(y).ActionVector.at(z).OtherHeadCode == SnsServiceRef) //OtherHeadCode values are service refs, see above
13522  { //reverse linkage found
13523  AVEntry0.LocationName = TrainDataVector.at(y).ActionVector.at(z).LocationName;
13524  AVEntry0.LocationType = AtLocation;
13525  BreakFlag = true;
13526  break;
13527  }
13528  }
13529  }
13530  if(BreakFlag)
13531  {
13532  break;
13533  }
13534  }
13535  //test for any unnamed AtLoc entries at end of name setting
13536  }
13537  }
13538 
13539 //trap errors where rsp/fsp follows an Sfs without a TimeLoc arrival before (or unlikely to be able to set fsp/rsp/Sfs location because Sfs locs set from linked fsp/rsp events)
13540  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13541  {
13542  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13543  if(AVEntry0.Command == "Sfs")
13544  {
13545  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
13546  {
13547  if(TrainDataVector.at(x).ActionVector.at(y).LocationName != "") //must be a timeloc as only they have loc set and are AtLoc (non-AtLoc trapped above)
13548  {
13549  break;
13550  }
13551  else if((TrainDataVector.at(x).ActionVector.at(y).Command == "fsp") || (TrainDataVector.at(x).ActionVector.at(y).Command == "rsp"))
13552  {
13553  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' event must be followed by a departure and arrival before another split, see " + TrainDataVector.at(x).ServiceReference);
13554  TrainDataVector.clear();
13555  Utilities->CallLogPop(2586);
13556  return(false);
13557  }
13558  }
13559  }
13560  }
13561 
13562 //now name fsp/rsp actions
13563  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13564  {
13565  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13566  {
13567  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13568  if(AVEntry.LocationName != "")
13569  {
13570  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
13571  {
13572  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
13573  // use reference so can change internals where necessary
13574  if((AVEntry2.Command == "fsp") || (AVEntry2.Command == "rsp"))
13575  {
13576  AVEntry2.LocationName = AVEntry.LocationName;
13577  } //test for any unnamed AtLoc entries at end of name setting
13578  else if(AVEntry2.LocationType != AtLocation)
13579  {
13580  break;
13581  }
13582  }
13583  }
13584  }
13585  }
13586 
13587 //check that all named or give error message
13588  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13589  {
13590  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13591  {
13592  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13593  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
13594  {
13595  if(AVEntry.LocationName == "")
13596  {
13597  SecondPassMessage(GiveMessages, "Error in timetable - an 'fsp' or 'rsp' event must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13598  TrainDataVector.clear();
13599  Utilities->CallLogPop(2617);
13600  return(false);
13601  }
13602  }
13603  }
13604  }
13605 
13606 //now set all Sfs entries from the above
13607  for(unsigned int x = 0; x < TrainDataVector.size(); x++) //at v2.15.0 set Sfs & Sns locations from corresponding fsp/rsp & Fns entries, shuttles ok as they are
13608  {
13609  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13610  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13611  // use reference so can change internals
13612  if(AVEntry0.Command == "Sfs")
13613  {
13614  //new at v2.15.0. Only set location if have a forward and backward linkage at same time (these all set when SecondPassActions called). This isn't
13615  //rigorous as may have more than one, but if do then will be caught below in CheckCrossReferencesAndSetData
13616  //If fail to find location just ignore as will be caught later in CheckCrossReferencesAndSetData
13617  //note that at this stage the OtherHeadCode values are service refs, as haven't yet been changed back to headcodes until StripExcessFromHeadCode called
13618  //at end of this function
13619  //need to be the same: forward & reverse service refs, event times, commands correspond
13620 
13621  //successor checks first: /no starts, /finishes except Frh & Fjo (as of v2.15.0), /repeats, /pas, /TimeTimeLoc or /TimeLoc arr; any other cmd or TimeLoc (dep) OK
13622  if(TDEntry.ActionVector.size() < 2)
13623  {
13624  SecondPassMessage(GiveMessages, "Error in timetable - insufficient actions follwing an 'Sfs' event for: " + TDEntry.HeadCode);
13625  TrainDataVector.clear();
13626  Utilities->CallLogPop(2587);
13627  return(false);
13628  }
13629  TActionVectorEntry AVEntry1 = TDEntry.ActionVector.at(1);
13630  if(!AtLocSuccessor(AVEntry1))
13631  {
13632  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' event is followed by an illegal event for: " + TDEntry.HeadCode +
13633  ". The event isn't valid for a stationary train.");
13634  TrainDataVector.clear();
13635  Utilities->CallLogPop(2588);
13636  return(false);
13637  }
13638  if((AVEntry1.SequenceType == StartSequence) || ((AVEntry1.SequenceType == FinishSequence) && (AVEntry1.Command != "Frh") && (AVEntry1.Command != "Fjo")) ||
13639  (AVEntry1.FormatType == Repeat))
13640  {
13641  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' event is followed by an illegal event for: " + TDEntry.HeadCode);
13642  TrainDataVector.clear();
13643  Utilities->CallLogPop(2589);
13644  return(false);
13645  }
13646 
13647  //now set the location and location type
13648  TDateTime SfsEventTime = AVEntry0.EventTime;
13649  AnsiString SfsServiceRef = TDEntry.ServiceReference;
13650  bool BreakFlag = false;
13651  for(unsigned int y = 0; y < TrainDataVector.size(); y++)
13652  {
13653  for(unsigned int z = 0; z < TrainDataVector.at(y).ActionVector.size(); z++)
13654  {
13655  if(((TrainDataVector.at(y).ActionVector.at(z).Command == "fsp") || (TrainDataVector.at(y).ActionVector.at(z).Command == "rsp")) &&
13656  (SfsEventTime == TrainDataVector.at(y).ActionVector.at(z).EventTime) && (TrainDataVector.at(y).HeadCode == AVEntry0.OtherHeadCode))
13657  { //forward linkage found
13658  if(TrainDataVector.at(y).ActionVector.at(z).OtherHeadCode == SfsServiceRef) //OtherHeadCode values are service refs, see above
13659  { //reverse linkage found
13660  AVEntry0.LocationName = TrainDataVector.at(y).ActionVector.at(z).LocationName;
13661  AVEntry0.LocationType = AtLocation;
13662  BreakFlag = true;
13663  break;
13664  }
13665  }
13666  }
13667  if(BreakFlag)
13668  {
13669  break;
13670  }
13671  } //test for any unnamed AtLoc entries at end of name setting
13672  }
13673  }
13674 
13675  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
13676  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13677  {
13678  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13679  {
13680  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13681  if(AVEntry.LocationName != "")
13682  {
13683  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
13684  {
13685  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
13686  // use reference so can change internals where necessary
13687  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
13688  {
13689  AVEntry2.LocationName = AVEntry.LocationName;
13690  } //test for any unnamed AtLoc entries at end of name setting
13691  else
13692  {
13693  break;
13694  }
13695  }
13696  }
13697  }
13698  }
13699  // all location names should now be set
13700 
13701 //now test for any unnamed AtLoc entries where Command != "" and give message if find any - shouldn't find any if above checks comprehensive
13702  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13703  {
13704  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13705  {
13706  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13707  if((AVEntry.LocationType == AtLocation) && (AVEntry.LocationName == "") && (AVEntry.Command != ""))
13708  {
13709  if((AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "cdt") || (AVEntry.Command == "dsc"))
13710  {
13711  SecondPassMessage(GiveMessages, "Error in timetable - '" + AVEntry.Command + "' must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13712  TrainDataVector.clear();
13713  Utilities->CallLogPop(2619);
13714  return(false);
13715  }
13716  else if((AVEntry.Command == "Sns") || (AVEntry.Command == "Sfs"))
13717  {
13718  SecondPassMessage(GiveMessages, "Error in timetable - the location of the '" + AVEntry.Command + "' event in service " + TrainDataVector.at(x).ServiceReference + " can't be identified. " +
13719  "Please make sure that the finish event of the service that links to this event is preceded by an "
13720  "event at the same location that has an identified location name, normally an arrival.");
13721  TrainDataVector.clear();
13722  Utilities->CallLogPop(2620);
13723  return(false);
13724  }
13725  else if(AVEntry.Command == "Snt-sh")
13726  {
13727  SecondPassMessage(GiveMessages, "Error in timetable - the location of the '" + AVEntry.Command + "' event in service " + TrainDataVector.at(x).ServiceReference + " can't be identified. " +
13728  "Please make sure that the service starts with zero speed and is at a named location.");
13729  TrainDataVector.clear();
13730  Utilities->CallLogPop(2623);
13731  return(false);
13732  }
13733  else if((AVEntry.Command == "Sns-fsh") || (AVEntry.Command == "Sns-sh"))
13734  {
13735  SecondPassMessage(GiveMessages, "Error in timetable - the location of the '" + AVEntry.Command + "' event in service " + TrainDataVector.at(x).ServiceReference + " can't be identified. " +
13736  "Please make sure that the event is followed (not necessarily immediately) by a departure.");
13737  TrainDataVector.clear();
13738  Utilities->CallLogPop(2622);
13739  return(false);
13740  }
13741  else if((AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"))
13742  {
13743  SecondPassMessage(GiveMessages, "Error in timetable - '" + AVEntry.Command + "must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13744  TrainDataVector.clear();
13745  Utilities->CallLogPop(2621);
13746  return(false);
13747  }
13748  }
13749  }
13750  }
13751 
13752  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
13753  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13754  {
13755  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13756  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13757  {
13758  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13759  if((AVEntry.SequenceType == FinishSequence) && (AVEntry.Command != "F-nshs"))
13760  {
13761  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
13762  // i.e at least one more, must be a repeat
13763  {
13764  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
13765  {
13766  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish event for: " + TDEntry.HeadCode);
13767  TrainDataVector.clear();
13768  Utilities->CallLogPop(798);
13769  return(false);
13770  }
13771  }
13772  }
13773  if(AVEntry.Command == "F-nshs")
13774  {
13775  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
13776  // i.e has to be the last
13777  {
13778  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
13779  TrainDataVector.clear();
13780  Utilities->CallLogPop(1049);
13781  return(false);
13782  }
13783  }
13784  if(AVEntry.Command == "pas")
13785  {
13786  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13787  {
13788  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
13789  TrainDataVector.clear();
13790  Utilities->CallLogPop(1518);
13791  return(false);
13792  }
13793  }
13794  if(AVEntry.Command == "jbo")
13795  {
13796  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13797  {
13798  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
13799  TrainDataVector.clear();
13800  Utilities->CallLogPop(800);
13801  return(false);
13802  }
13803  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13804  if(!AtLocSuccessor(AVEntry2))
13805  {
13806  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
13807  ". The event isn't valid for a stationary train.");
13808  TrainDataVector.clear();
13809  Utilities->CallLogPop(801);
13810  return(false);
13811  }
13812  }
13813  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
13814  {
13815  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13816  {
13817  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
13818  TrainDataVector.clear();
13819  Utilities->CallLogPop(802);
13820  return(false);
13821  }
13822  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13823  if(!AtLocSuccessor(AVEntry2))
13824  {
13825  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
13826  ". The event isn't valid for a stationary train.");
13827  TrainDataVector.clear();
13828  Utilities->CallLogPop(803);
13829  return(false);
13830  }
13831  }
13832  if(AVEntry.Command == "cdt")
13833  {
13834  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13835  {
13836  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
13837  TrainDataVector.clear();
13838  Utilities->CallLogPop(804);
13839  return(false);
13840  }
13841  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13842  if(!AtLocSuccessor(AVEntry2))
13843  {
13844  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
13845  ". The event isn't valid for a stationary train.");
13846  TrainDataVector.clear();
13847  Utilities->CallLogPop(805);
13848  return(false);
13849  }
13850  }
13851  if(AVEntry.Command == "dsc")
13852  {
13853  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13854  {
13855  SecondPassMessage(GiveMessages, "Error in timetable - a 'dsc' can't be the last event for: " + TDEntry.HeadCode);
13856  TrainDataVector.clear();
13857  Utilities->CallLogPop(2602);
13858  return(false);
13859  }
13860  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13861  if(!AtLocSuccessor(AVEntry2))
13862  {
13863  SecondPassMessage(GiveMessages, "Error in timetable - a 'dsc' is followed by an illegal event for: " + TDEntry.HeadCode +
13864  ". The event isn't valid for a stationary train.");
13865  TrainDataVector.clear();
13866  Utilities->CallLogPop(2603);
13867  return(false);
13868  }
13869  }
13870  if(AVEntry.FormatType == TimeTimeLoc)
13871  {
13872  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13873  {
13874  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
13875  TrainDataVector.clear();
13876  Utilities->CallLogPop(806);
13877  return(false);
13878  }
13879  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13880  if(!MovingSuccessor(AVEntry2))
13881  {
13882  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
13883  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
13884  TrainDataVector.clear();
13885  Utilities->CallLogPop(807);
13886  return(false);
13887  }
13888  }
13889  if(AVEntry.FormatType == PassTime)
13890  {
13891  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13892  {
13893  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
13894  TrainDataVector.clear();
13895  Utilities->CallLogPop(1530);
13896  return(false);
13897  }
13898  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13899  if(!MovingSuccessor(AVEntry2))
13900  {
13901  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
13902  ". The event isn't valid for a moving train.");
13903  TrainDataVector.clear();
13904  Utilities->CallLogPop(1531);
13905  return(false);
13906  }
13907  }
13908  if(AVEntry.FormatType == Repeat)
13909  {
13910  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
13911  {
13912  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
13913  TrainDataVector.clear();
13914  Utilities->CallLogPop(808);
13915  return(false);
13916  }
13917  }
13918  }
13919  }
13920 
13921  // set arrival & departure times for TimeLocs & set their EventTimes to -1
13922  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13923  {
13924  bool LastEntryIsAnArrival = false;
13925  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
13926  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
13927  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13928  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
13929  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
13930  {
13931  LastEntryIsAnArrival = false;
13932  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13933  {
13934  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13935  if(AVEntry.FormatType == TimeLoc)
13936  {
13937  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
13938  {
13939  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
13940  }
13941  if(LastEntryIsAnArrival)
13942  {
13943  AVEntry.DepartureTime = AVEntry.EventTime;
13944  AVEntry.EventTime = TDateTime(-1);
13945  LastEntryIsAnArrival = false;
13946  }
13947  else // last entry a departure
13948  {
13949  AVEntry.ArrivalTime = AVEntry.EventTime;
13950  AVEntry.EventTime = TDateTime(-1);
13951  LastEntryIsAnArrival = true;
13952  }
13953  }
13954  }
13955  }
13956  else // all others stopped at beginning
13957  {
13958  LastEntryIsAnArrival = true;
13959  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13960  {
13961  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13962  if(AVEntry.FormatType == TimeLoc)
13963  {
13964  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
13965  {
13966  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
13967  }
13968  if(LastEntryIsAnArrival)
13969  {
13970  AVEntry.DepartureTime = AVEntry.EventTime;
13971  AVEntry.EventTime = TDateTime(-1);
13972  LastEntryIsAnArrival = false;
13973  }
13974  else // last entry a departure
13975  {
13976  AVEntry.ArrivalTime = AVEntry.EventTime;
13977  AVEntry.EventTime = TDateTime(-1);
13978  LastEntryIsAnArrival = true;
13979  }
13980  }
13981  }
13982  }
13983  }
13984  // perform remaining successor checks for TimeLocs
13985  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13986  {
13987  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13988  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13989  {
13990  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13991  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
13992  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
13993  {
13994  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13995  {
13996  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
13997  TrainDataVector.clear();
13998  Utilities->CallLogPop(809);
13999  return(false);
14000  }
14001  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14002  if(!AtLocSuccessor(AVEntry2))
14003  {
14004  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
14005  ". The event isn't valid for a stationary train.");
14006  TrainDataVector.clear();
14007  Utilities->CallLogPop(810);
14008  return(false);
14009  }
14010  }
14011  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
14012  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
14013  {
14014  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14015  {
14016  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
14017  TrainDataVector.clear();
14018  Utilities->CallLogPop(811);
14019  return(false);
14020  }
14021  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14022  if(!MovingSuccessor(AVEntry2))
14023  {
14024  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
14025  ". The event isn't valid for a moving train.");
14026  TrainDataVector.clear();
14027  Utilities->CallLogPop(812);
14028  return(false);
14029  }
14030  }
14031  }
14032  }
14033 
14034  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
14035  // & repeats have no times set
14036  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14037  {
14038  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14039  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14040  {
14041  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14042  if(AVEntry.FormatType == TimeLoc)
14043  {
14044  if(AVEntry.EventTime != TDateTime(-1))
14045  {
14046  throw Exception("Timetable error, TimeLoc event has EventTime not -1 for " + TDEntry.HeadCode);
14047  }
14048  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
14049  {
14050  throw Exception("Timetable error, TimeLoc event has neither arrival nor departure time set for " + TDEntry.HeadCode);
14051  }
14052  }
14053  if(AVEntry.FormatType == TimeTimeLoc)
14054  {
14055  if(AVEntry.EventTime != TDateTime(-1))
14056  {
14057  throw Exception("Timetable error, TimeTimeLoc event has EventTime not -1 for " + TDEntry.HeadCode);
14058  }
14059  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
14060  {
14061  throw Exception("Timetable error, TimeTimeLoc event has either arrival or departure time not set for " + TDEntry.HeadCode);
14062  }
14063  }
14064  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
14065  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
14066  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
14067  {
14068  if(AVEntry.EventTime == TDateTime(-1))
14069  {
14070  throw Exception("Timetable error, Cmd or PassTime event has EventTime not set for " + TDEntry.HeadCode);
14071  }
14072  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
14073  {
14074  throw Exception("Timetable error, Cmd or PassTime event has either arrival or departure time set for " + TDEntry.HeadCode);
14075  }
14076  }
14077  if(AVEntry.FormatType == Repeat)
14078  {
14079  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
14080  {
14081  throw Exception("Timetable error, Repeat event has a time set for " + TDEntry.HeadCode);
14082  }
14083  }
14084  }
14085  }
14086 
14087  // check times stay same or increase, note that can have time of 0 if include midnight
14088  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14089  {
14090  TDateTime CurrentTime = TTClockTime; // the timetable start time
14091  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14092  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14093  {
14094  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14095  if(AVEntry.FormatType == Repeat)
14096  {
14097  break;
14098  }
14099  if(AVEntry.FormatType == FinRemHere)
14100  {
14101  break;
14102  }
14103  if(AVEntry.FormatType == TimeTimeLoc)
14104  {
14105  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
14106  {
14107  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
14108  TDEntry.HeadCode);
14109  TrainDataVector.clear();
14110  Utilities->CallLogPop(813);
14111  return(false);
14112  }
14113  if(AVEntry.ArrivalTime < CurrentTime)
14114  {
14115  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
14116  TDEntry.HeadCode);
14117  TrainDataVector.clear();
14118  Utilities->CallLogPop(814);
14119  return(false);
14120  }
14121  CurrentTime = AVEntry.DepartureTime;
14122  continue;
14123  }
14124  if(AVEntry.FormatType == TimeLoc)
14125  {
14126  if(AVEntry.ArrivalTime >= TDateTime(0))
14127  {
14128  if(AVEntry.ArrivalTime < CurrentTime)
14129  {
14130  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
14131  TrainDataVector.clear();
14132  Utilities->CallLogPop(815);
14133  return(false);
14134  }
14135  CurrentTime = AVEntry.ArrivalTime;
14136  }
14137  else
14138  {
14139  if(AVEntry.DepartureTime < CurrentTime)
14140  // both may be 0 legitimately so must allow for this
14141  {
14142  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
14143  TrainDataVector.clear();
14144  Utilities->CallLogPop(816);
14145  return(false);
14146  }
14147  CurrentTime = AVEntry.DepartureTime;
14148  }
14149  continue;
14150  }
14151  if(AVEntry.EventTime < CurrentTime)
14152  // all others have EventTime set
14153  {
14154  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
14155  ", may be before timetable start time");
14156  TrainDataVector.clear();
14157  Utilities->CallLogPop(835);
14158  return(false);
14159  }
14160  CurrentTime = AVEntry.EventTime;
14161  continue;
14162  }
14163  }
14164 
14165  // check locations consistent
14166  AnsiString LastLocationName = "";
14167 
14168  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14169  {
14170  bool LastEntryIsAnArrival = false;
14171  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14172  // first deal with moving Snt entries (all else stopped)
14173  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
14174  {
14175  LastEntryIsAnArrival = false;
14176  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
14177  if(LastLocationName != "")
14178  {
14179  throw Exception("Timetable error, moving Snt event has LocationName set for " + TDEntry.HeadCode);
14180  }
14181  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
14182  y++) // note that immediate successor to a moving Snt can only be a Moving type
14183  {
14184  // if it's a SignallerControl entry then the condition isn't met
14185  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14186  if(AVEntry.FormatType == Repeat)
14187  {
14188  break; // repeat = reached end (+allows repeat after signaller controlled entry)
14189  }
14190  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
14191  {
14192  if(AVEntry.LocationName != LastLocationName)
14193  {
14194  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14195  AVEntry.Command);
14196  TrainDataVector.clear();
14197  Utilities->CallLogPop(823);
14198  return(false);
14199  }
14200  }
14201  else if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdDescription))
14202  // cdt is the only TimeCmd & dsc is the only TimeCmdDescription
14203  {
14204  if(AVEntry.LocationName != LastLocationName)
14205  {
14206  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14207  AVEntry.Command);
14208  TrainDataVector.clear();
14209  Utilities->CallLogPop(824);
14210  return(false);
14211  }
14212  }
14213  else if(AVEntry.FormatType == TimeTimeLoc)
14214  {
14215  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
14216  // last entry must be a departure or would have failed earlier
14217  {
14218  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
14219  TwoLocationFlag = true;
14220 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
14221 // TwoOrMoreLocationsWarningGiven = true;
14222  }
14223  LastLocationName = AVEntry.LocationName;
14224  LastEntryIsAnArrival = false;
14225  }
14226  else if(AVEntry.FormatType == TimeLoc)
14227  {
14228  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
14229  {
14230  SecondPassMessage(GiveMessages,
14231  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
14232  TrainDataVector.clear();
14233  Utilities->CallLogPop(826);
14234  return(false);
14235  }
14236  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
14237  {
14238  SecondPassMessage(GiveMessages,
14239  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
14240  TrainDataVector.clear();
14241  Utilities->CallLogPop(827);
14242  return(false);
14243  }
14244  LastLocationName = AVEntry.LocationName;
14245  LastEntryIsAnArrival = !LastEntryIsAnArrival;
14246  }
14247  }
14248  }
14249  else // all stationary starting entries
14250  {
14251  LastEntryIsAnArrival = true;
14252  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
14253  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14254  {
14255  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14256  if(AVEntry.FormatType == Repeat)
14257  {
14258  break;
14259  }
14260  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
14261  // no need to add anything for shuttle starts since they are at loc (0) anyway
14262  {
14263  if(AVEntry.LocationName != LastLocationName)
14264  {
14265  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14266  AVEntry.Command);
14267  TrainDataVector.clear();
14268  Utilities->CallLogPop(828);
14269  return(false);
14270  }
14271  }
14272  else if(AVEntry.FormatType == TimeCmd)
14273  // cdt is the only TimeCmd
14274  {
14275  if(AVEntry.LocationName != LastLocationName)
14276  {
14277  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14278  AVEntry.Command);
14279  TrainDataVector.clear();
14280  Utilities->CallLogPop(829);
14281  return(false);
14282  }
14283  }
14284  else if(AVEntry.FormatType == TimeTimeLoc)
14285  {
14286  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
14287  // last entry must be a departure or would have failed earlier
14288  {
14289  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
14290  TwoLocationFlag = true;
14291 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
14292 // TwoOrMoreLocationsWarningGiven = true;
14293  }
14294  LastLocationName = AVEntry.LocationName;
14295  LastEntryIsAnArrival = false;
14296  }
14297  else if(AVEntry.FormatType == TimeLoc)
14298  {
14299  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
14300  {
14301  SecondPassMessage(GiveMessages,
14302  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
14303  TrainDataVector.clear();
14304  Utilities->CallLogPop(831);
14305  return(false);
14306  }
14307  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
14308  {
14309  SecondPassMessage(GiveMessages,
14310  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
14312 // TrainDataVector.clear();
14313 // Utilities->CallLogPop(832);
14314 // return false;
14315  }
14316  LastLocationName = AVEntry.LocationName;
14317  LastEntryIsAnArrival = !LastEntryIsAnArrival;
14318  }
14319  }
14320  }
14321  }
14322 
14323  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
14324  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
14325  AnsiString LocationNameToBeChecked = "";
14326 
14327  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14328  {
14329  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14330  unsigned int y = 0;
14331  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
14332  // first discard unlocated Snt entries as they don't have location name set
14333  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
14334  {
14335  y = 1;
14336  }
14337  while(y < TDEntry.ActionVector.size())
14338  // need to check each location name separately in turn, skipped for SignallerControl entries
14339  {
14340  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
14341  {
14342  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
14343  }
14344  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
14345  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
14346  {
14347  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
14348  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
14349  {
14350  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
14351  }
14352  if(AVEntry.Command == "cdt")
14353  {
14354  break; // out of the 'z' loop since the check is only valid up to a change of direction
14355  }
14356  if(AVEntry.LocationName == LocationNameToBeChecked)
14357  {
14358  continue; // keep going while name same
14359  }
14360  if(AVEntry.LocationName != LocationNameToBeChecked)
14361  // if name different check forwards to see if repeats
14362  {
14363  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
14364  {
14365  if(TDEntry.ActionVector.at(a).Command == "cdt")
14366  {
14367  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
14368  }
14369  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
14370  {
14371  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
14372  TwoLocationFlag = true;
14373 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
14374 // TwoOrMoreLocationsWarningGiven = true;
14375  }
14376  }
14377  break; // out of the 'z' loop since have checked 'a' as far as need to
14378  }
14379  }
14380  y++;
14381  }
14382  }
14383  if(TwoLocationFlag) //messages for this moved to InterfaceUnit - separate box listing potential errors and asking user to check
14384  {
14385  TwoLocationList.sort(); //need to sort first in alphabetical order to ensure all duplictes removed
14386  TwoLocationList.unique(); //remove duplicates
14387  }
14388 
14389  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
14390  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14391  {
14392  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14393  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14394  {
14395  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14396  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
14397  {
14398  throw Exception("Error, non- 'Snt', 'Fer' or Repeat event doesn't have a location name set for " + TDEntry.HeadCode);
14399  }
14400  AnsiString LocName = "";
14401  // dummy, only used so can call IsSNTEntryLocated
14402  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
14403  {
14404  if(AVEntry.LocationName == "")
14405  {
14406  throw Exception("Error, 'Snt' event at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
14407  }
14408  }
14409  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
14410  {
14411  if(AVEntry.LocationName != "")
14412  {
14413  throw Exception("Error, 'Snt' unlocated event has a location name set for " + TDEntry.HeadCode);
14414  }
14415  }
14416  }
14417  }
14418 
14419 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
14420  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
14421  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
14422 
14423  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
14424  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
14425 
14426  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
14427  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
14428  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
14429 */
14430  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
14431  {
14432  // non-shuttles & non-repeating links separately, but don't check that there isn't a
14433  // duplicate between a non-repeating shuttle and another - leave original tests in as
14434  // these also set the pointers
14435  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14436  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14437  {
14438  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14439  if(AVEntry.OtherHeadCode != "")
14440  {
14441  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
14442  {
14443  Utilities->CallLogPop(1584);
14444  return(false); // error message given in called function
14445  }
14446  }
14447  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
14448  {
14449  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
14450  {
14451  Utilities->CallLogPop(1585);
14452  return(false); // error message given in called function
14453  }
14454  }
14455  }
14456  }
14457 
14458  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14459  {
14460  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14461  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14462  {
14463  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14464  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
14465  {
14466  if(AVEntry.OtherHeadCode != "")
14467  {
14468  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, true, GiveMessages))
14469  // false = non-shuttle
14470  {
14471  Utilities->CallLogPop(864);
14472  return(false); // error message given in called function
14473  }
14474  }
14475  }
14476  }
14477  }
14478 
14479  // now repeat the check just for the shuttles
14480  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14481  {
14482  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14483  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14484  {
14485  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14486  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
14487  {
14488  if(AVEntry.OtherHeadCode != "")
14489  {
14490  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, true, GiveMessages))
14491  // true = shuttle
14492  {
14493  Utilities->CallLogPop(1100);
14494  return(false); // error message given in called function
14495  }
14496  }
14497  }
14498  }
14499  }
14500 
14501  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14502  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14503  {
14504  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14505  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14506  {
14507  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14508  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
14509  {
14510  if(!CheckNonRepeatingShuttleLinksAndSetData(0, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, true, GiveMessages))
14511  {
14512  Utilities->CallLogPop(1060);
14513  return(false); // error message given in called function
14514  }
14515  }
14516  }
14517  }
14518 
14519  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
14520  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
14521  // don't ever need to and as designed would skip repeats
14522  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14523  {
14524  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14525  {
14526  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14527  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
14528  {
14529  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
14530  {
14531  Utilities->CallLogPop(1090);
14532  return(false); // error message given in called function
14533  }
14534  }
14535  }
14536  }
14537 
14538  // check all entries have all types set to something
14539  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14540  {
14541  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14542  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14543  {
14544  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14545  if(AVEntry.FormatType == NoFormat)
14546  {
14547  throw Exception("Error - timetable ActionVector event no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
14548  }
14549  else if(AVEntry.SequenceType == NoSequence)
14550  {
14551  throw Exception("Error - timetable ActionVector event no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
14552  }
14553  else if(AVEntry.LocationType == NoLocation)
14554  {
14555  throw Exception("Error - timetable ActionVector event no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
14556  }
14557  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
14558  {
14559  throw Exception("Error - timetable ActionVector event no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
14560  }
14561  }
14562  }
14563 
14564  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
14565  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14566  {
14567  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14568  // non-const reference so can alter content
14569  TTrainOperatingData TData;
14570  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
14571  if(LastAVEntry.FormatType == Repeat) // check if a repeat
14572  {
14573 /*
14574  class TTrainOperatingData
14575  {
14576  public:
14577  int TrainID; - default, set at construction
14578  TActionEventType EventReported; used during operation
14579  TRunningEntry RunningEntry; - default, set at construction
14580  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
14581  };
14582 */
14583  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
14584  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
14585  {
14586  TDEntry.TrainOperatingDataVector.push_back(TData);
14587  }
14588  }
14589  else
14590  {
14591  TDEntry.NumberOfTrains = 1;
14592  }
14593  }
14594 
14595  // check that don't include any Continuation names
14596  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14597  {
14598  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14599  {
14600  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
14601  AnsiString HC = TrainDataVector.at(x).HeadCode;
14602  if(LocName != "")
14603  {
14604  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
14605  {
14606  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
14607  TrainDataVector.clear();
14608  Utilities->CallLogPop(1578);
14609  return(false);
14610  }
14611  }
14612  }
14613  }
14614 
14615  // check that all repeat times below 96h
14616  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14617  {
14618  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
14619  int IncMinutes = 0;
14620  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
14621  {
14622  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
14623  }
14624  else
14625  {
14626  continue; // basic times already checked in CheckTimeValidity
14627  }
14628  AnsiString HC = TrainDataVector.at(x).HeadCode;
14629  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14630  {
14631  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
14632  {
14633  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14634  {
14635  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
14636  TrainDataVector.clear();
14637  Utilities->CallLogPop(1818);
14638  return(false);
14639  }
14640  }
14641  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
14642  {
14643  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14644  // 3d 23h 59m = 3.9993055556
14645  {
14646  SecondPassMessage(GiveMessages, "Error in timetable - a repeat event time exceeds 95h 59m, see service " + HC);
14647  TrainDataVector.clear();
14648  Utilities->CallLogPop(1819);
14649  return(false);
14650  }
14651  }
14652  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
14653  {
14654  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14655  // 3d 23h 59m = 3.9993055556
14656  {
14657  SecondPassMessage(GiveMessages, "Error in timetable - a repeat event time exceeds 95h 59m, see service " + HC);
14658  TrainDataVector.clear();
14659  Utilities->CallLogPop(1820);
14660  return(false);
14661  }
14662  }
14663  }
14664  }
14665 
14666  // Now that all set up change any extended headcodes back to ordinary headcodes
14667  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14668  {
14669  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
14670  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14671  {
14672  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
14673  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
14674  }
14675  }
14676 
14677  // SaveTrainDataVectorToFile(0);//for testing purposes
14679  Utilities->CallLogPop(782);
14680  return(true);
14681 }
14682 
14683 // ---------------------------------------------------------------------------
14684 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
14686 {
14687  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
14688 }
14689 
14690 // ---------------------------------------------------------------------------
14691 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/dsc/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
14693 {
14694  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
14695  (AVEntry.Command == "cdt") || (AVEntry.Command == "dsc") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
14696  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
14697 }
14698 
14699 // ---------------------------------------------------------------------------
14700 
14701 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
14702 {
14703  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
14704  if(HeadCode.Length() > 4) // ignore otherwise
14705  {
14706  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
14707  }
14708  Utilities->CallLogPop(1593);
14709 }
14710 
14711 // ---------------------------------------------------------------------------
14712 
14713 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
14714 {
14715  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
14716  SecondHeadCode);
14717  int ForwardCount = 0;
14718  int ReverseCount = 0;
14719 
14720  if(MainHeadCode == SecondHeadCode)
14721  {
14722  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
14723  TrainDataVector.clear();
14724  Utilities->CallLogPop(1594);
14725  return(false);
14726  }
14727  // forward check
14728  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14729  {
14730  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14731  if(TDEntry.HeadCode == MainHeadCode)
14732  {
14733  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14734  {
14735  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14736  if(AVEntry.OtherHeadCode == SecondHeadCode)
14737  {
14738  ForwardCount++;
14739  }
14740  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
14741  // need own check in case both 'Other' & 'NonRepeating' have same headcode
14742  {
14743  ForwardCount++;
14744  }
14745  }
14746  }
14747  }
14748  if(ForwardCount == 0)
14749  // this is an exception because the headcodes are selected in the same order as the forward check
14750  {
14751  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
14752  }
14753  if(ForwardCount > 2)
14754  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
14755  {
14756  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
14757  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
14758  TrainDataVector.clear();
14759  Utilities->CallLogPop(1587);
14760  return(false);
14761  }
14762  // reverse check
14763  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14764  {
14765  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14766  if(TDEntry.HeadCode == SecondHeadCode)
14767  {
14768  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14769  {
14770  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14771  if(AVEntry.OtherHeadCode == MainHeadCode)
14772  {
14773  ReverseCount++;
14774  }
14775  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
14776  {
14777  ReverseCount++;
14778  }
14779  }
14780  }
14781  }
14782 
14783  if(ReverseCount == 0)
14784  {
14785  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
14786  TrainDataVector.clear();
14787  Utilities->CallLogPop(1588);
14788  return(false);
14789  }
14790  if(ReverseCount > 2)
14791  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
14792  {
14793  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
14794  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
14795  TrainDataVector.clear();
14796  Utilities->CallLogPop(1589);
14797  return(false);
14798  }
14799  if(ForwardCount != ReverseCount)
14800  {
14801  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
14802  " than the other way round");
14803  TrainDataVector.clear();
14804  Utilities->CallLogPop(1610);
14805  return(false);
14806  }
14807  Utilities->CallLogPop(1590);
14808  return(true);
14809 }
14810 
14811 // ---------------------------------------------------------------------------
14812 
14813 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool SetDataAndCheckLocations, bool GiveMessages)
14814 /* Return false for no find or more than one find, check correct types of link
14815  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
14816  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
14817  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
14818  Then do the same in reverse.
14819  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
14820  if main is Fns other must be Sns; if main is jbo other must be Fjo.
14821  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
14822  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
14823  for Sfs & Sns services. Finally check the repeat entries if present are consistent
14824 
14825  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
14826  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
14827 
14828  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
14829 */
14830 
14831 {
14832  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
14833  int ForwardCount = 0;
14834  int ReverseCount = 0;
14835  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
14836  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
14837  TTrainDataEntry *MainTrainDataPtr = 0;
14838  TTrainDataEntry *OtherTrainDataPtr = 0;
14839 
14840  // forward check
14841  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14842  {
14843  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14844  if(TDEntry.HeadCode == MainHeadCode)
14845  {
14846  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14847  {
14848  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14849  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
14850  {
14851  if(AVEntry.OtherHeadCode == OtherHeadCode)
14852  {
14853  MainTrainDataPtr = &TrainDataVector.at(x);
14854  ForwardEntryPtr = &AVEntry;
14855  ForwardCount++;
14856  ForwardTDVectorNumber = x;
14857  }
14858  }
14859  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
14860  (AVEntry.Command == "Frh-sh")))
14861  {
14862  if(AVEntry.OtherHeadCode == OtherHeadCode)
14863  {
14864  MainTrainDataPtr = &TrainDataVector.at(x);
14865  ForwardEntryPtr = &AVEntry;
14866  ForwardCount++;
14867  ForwardTDVectorNumber = x;
14868  }
14869  }
14870  }
14871  }
14872  }
14873  if(ForwardCount == 0)
14874  // this is an exception because the headcodes are selected in the same order as the forward check
14875  {
14876  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
14877  }
14878  if(ForwardCount > 1)
14879  {
14880  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
14881  MainHeadCode);
14882  TrainDataVector.clear();
14883  Utilities->CallLogPop(836);
14884  return(false);
14885  }
14886  // reverse check
14887  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14888  {
14889  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14890  if(TDEntry.HeadCode == OtherHeadCode)
14891  {
14892  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14893  {
14894  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14895  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
14896  {
14897  if(AVEntry.OtherHeadCode == MainHeadCode)
14898  {
14899  OtherTrainDataPtr = &TrainDataVector.at(x);
14900  ReverseCount++;
14901  ReverseEntryPtr = &AVEntry;
14902  ReverseTDVectorNumber = x;
14903  }
14904  }
14905  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
14906  {
14907  if(AVEntry.OtherHeadCode == MainHeadCode)
14908  {
14909  OtherTrainDataPtr = &TrainDataVector.at(x);
14910  ReverseCount++;
14911  ReverseEntryPtr = &AVEntry;
14912  ReverseTDVectorNumber = x;
14913  }
14914  }
14915  }
14916  }
14917  }
14918 
14919  if(ReverseCount == 0)
14920  {
14921  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
14922  TrainDataVector.clear();
14923  Utilities->CallLogPop(837);
14924  return(false);
14925  }
14926  if(ReverseCount > 1)
14927  {
14928  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
14929  OtherHeadCode);
14930  TrainDataVector.clear();
14931  Utilities->CallLogPop(838);
14932  return(false);
14933  }
14934  // these will all be false for !Shuttle
14935  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
14936  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
14937  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
14938  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
14939 
14940  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
14941  {
14942  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
14943  TrainDataVector.clear();
14944  Utilities->CallLogPop(1058);
14945  return(false);
14946  }
14947  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
14948  {
14949  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
14950  TrainDataVector.clear();
14951  Utilities->CallLogPop(1059);
14952  return(false);
14953  }
14954  if(SetDataAndCheckLocations)
14955  {
14956  if(ForwardEntryPtr->LocationName == "")
14957  {
14958  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
14959  ". One or other service does not have a location set");
14960  TrainDataVector.clear();
14961  Utilities->CallLogPop(526);
14962  return(false);
14963  }
14964  if(ReverseEntryPtr->LocationName == "")
14965  {
14966  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
14967  ". One or other service does not have a location set");
14968  TrainDataVector.clear();
14969  Utilities->CallLogPop(527);
14970  return(false);
14971  }
14972  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
14973  {
14974  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
14975  " is at a different location to the referencing train " + MainHeadCode);
14976  TrainDataVector.clear();
14977  Utilities->CallLogPop(842);
14978  return(false);
14979  }
14980  }
14981  // ignore shuttle repeat links for first time check
14982  if(!Shuttle)
14983  {
14984  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
14985  {
14986  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
14987  " has a different event time to the referencing train " + MainHeadCode);
14988  TrainDataVector.clear();
14989  Utilities->CallLogPop(525);
14990  return(false);
14991  }
14992  }
14993  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
14994  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
14995  if(ForwardShuttleStart && ReverseShuttleFinish)
14996  // Shuttle must be true if these are true
14997  {
14998  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
14999  {
15000  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
15001  " first repeat restart time not consistent with finish service " + OtherHeadCode);
15002  TrainDataVector.clear();
15003  Utilities->CallLogPop(1055);
15004  return(false);
15005  }
15006  }
15007  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
15008  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
15009  {
15010  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15011  {
15012  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
15013  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
15014  TrainDataVector.clear();
15015  Utilities->CallLogPop(528);
15016  return(false);
15017  }
15018  }
15019  if(ReverseEntryPtr->Command == "Fjo")
15020  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
15021  {
15022  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15023  {
15024  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
15025  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
15026  TrainDataVector.clear();
15027  Utilities->CallLogPop(862);
15028  return(false);
15029  }
15030  }
15031  if(ReverseEntryPtr->Command == "Fns")
15032  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
15033  {
15034  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15035  {
15036  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
15037  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
15038  TrainDataVector.clear();
15039  Utilities->CallLogPop(529);
15040  return(false);
15041  }
15042  }
15043  if(ForwardEntryPtr->Command == "Sfs")
15044  {
15045  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
15046  {
15047  SecondPassMessage(GiveMessages,
15048  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
15049  MainHeadCode);
15050  TrainDataVector.clear();
15051  Utilities->CallLogPop(530);
15052  return(false);
15053  }
15054  }
15055  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
15056  {
15057  if(ReverseEntryPtr->Command != "Sfs")
15058  {
15059  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
15060  MainHeadCode);
15061  TrainDataVector.clear();
15062  Utilities->CallLogPop(839);
15063  return(false);
15064  }
15065  else
15066  {
15067  if(SetDataAndCheckLocations)
15068  {
15069  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
15070  {
15071  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
15072  TrainDataVector.clear();
15073  Utilities->CallLogPop(849);
15074  return(false);
15075  }
15076  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
15077  {
15078  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
15079  TrainDataVector.clear();
15080  Utilities->CallLogPop(850);
15081  return(false);
15082  }
15083 //determine whether LocationName is a station or non-station
15084  bool StationLocation = false;
15085  for(TTrack::TTrackVectorIterator TEIt = Track->InactiveTrackVector.begin(); TEIt != Track->InactiveTrackVector.end(); TEIt++)
15086  {
15087  if(TEIt->LocationName == ForwardEntryPtr->LocationName)
15088  {
15089  if(TEIt->TrackType != NamedNonStationLocation)
15090  {
15091  StationLocation = true;
15092  }
15093  }
15094  }
15095  if(StationLocation)
15096  {
15097  if(!(Track->OneStationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
15098  {
15099  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
15100  TrainDataVector.clear();
15101  Utilities->CallLogPop(846);
15102  return(false);
15103  }
15104  }
15105  else
15106  {
15107  if(!(Track->OneNonStationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
15108  {
15109  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
15110  TrainDataVector.clear();
15111  Utilities->CallLogPop(2660);
15112  return(false);
15113  }
15114  }
15115  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15116  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15117  if(OtherTrainDataPtr->FixedDescription == "") //name changed at v2.16.1
15118  {
15119  OtherTrainDataPtr->FixedDescription = MainTrainDataPtr->FixedDescription;
15120  }
15121  }
15122  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
15123  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
15124  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15125  }
15126  }
15127  if(ForwardEntryPtr->Command == "Sns")
15128  {
15129  if(ReverseEntryPtr->Command != "Fns")
15130  {
15131  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
15132  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
15133  TrainDataVector.clear();
15134  Utilities->CallLogPop(531);
15135  return(false);
15136  }
15137  }
15138  if(ForwardEntryPtr->Command == "Fns")
15139  {
15140  if(ReverseEntryPtr->Command != "Sns")
15141  {
15142  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
15143  " and forms a new service with headcode " + OtherHeadCode);
15144  TrainDataVector.clear();
15145  Utilities->CallLogPop(840);
15146  return(false);
15147  }
15148  else
15149  {
15150  if(SetDataAndCheckLocations)
15151  {
15152  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15153  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15154  if(OtherTrainDataPtr->FixedDescription == "") //name changed at v2.16.1
15155  {
15156  OtherTrainDataPtr->FixedDescription = MainTrainDataPtr->FixedDescription;
15157  }
15158  }
15159  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15160  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
15161  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15162  }
15163  }
15164  if(ForwardEntryPtr->Command == "jbo")
15165  {
15166  if(ReverseEntryPtr->Command != "Fjo")
15167  {
15168  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
15169  " and is joined by a train with headcode " + OtherHeadCode);
15170  TrainDataVector.clear();
15171  Utilities->CallLogPop(841);
15172  return(false);
15173  }
15174  }
15175  if(ForwardEntryPtr->Command == "Fjo")
15176  {
15177  if(ReverseEntryPtr->Command != "jbo")
15178  {
15179  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
15180  " and joins a train with headcode " + OtherHeadCode);
15181  TrainDataVector.clear();
15182  Utilities->CallLogPop(532);
15183  return(false);
15184  }
15185  else
15186  {
15187  if(SetDataAndCheckLocations)
15188  {
15189  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15190  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15191  }
15192 /*
15193  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
15194  {
15195  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed; //this should only be set when the trains join
15196  } not when internal timetable being compiled. Dropped at v2.15.0 when Brent Mackie reported error on 18/02/23 via discord
15197 
15198  //added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a
15199  //zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
15200  //notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the
15201  //'joined by' train's max speed is less.
15202 */
15203  }
15204  }
15205  if(ForwardShuttleStart)
15206  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
15207  {
15208  if(!ReverseShuttleFinish)
15209  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
15210  {
15211  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
15212  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
15213  TrainDataVector.clear();
15214  Utilities->CallLogPop(1056);
15215  return(false);
15216  }
15217  }
15218  if(ReverseShuttleStart)
15219  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
15220  {
15221  if(!ForwardShuttleFinish)
15222  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
15223  {
15224  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
15225  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
15226  TrainDataVector.clear();
15227  Utilities->CallLogPop(1057);
15228  return(false);
15229  }
15230  else
15231  {
15232  if(SetDataAndCheckLocations)
15233  {
15234  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15235  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15236  }
15237 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
15238  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
15239 */
15240  }
15241  }
15242  // check repeat information consistent if present
15243  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
15244  // and those not accessed here
15245 
15246  // still need to check the non-repeating links and that they have no repeats - do that outside this function
15247  bool MainRepeat = false, OtherRepeat = false;
15248  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
15249 
15250  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
15251  {
15252  MainRepeat = true;
15253  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
15254  }
15255  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
15256  {
15257  OtherRepeat = true;
15258  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
15259  }
15260  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
15261  {
15262  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
15263  " and the associated train with headcode " + OtherHeadCode);
15264  TrainDataVector.clear();
15265  Utilities->CallLogPop(844);
15266  return(false);
15267  }
15268  if(MainRepeat && OtherRepeat)
15269  {
15270  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
15271  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
15272  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
15273  {
15274  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
15275  " and the associated train with headcode " + OtherHeadCode);
15276  TrainDataVector.clear();
15277  Utilities->CallLogPop(845);
15278  return(false);
15279  }
15280  }
15281  Utilities->CallLogPop(863);
15282  return(true);
15283 }
15284 
15285 // ---------------------------------------------------------------------------
15286 
15287 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
15288 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
15289 {
15290  // strip spaces from extreme ends of input
15291  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
15292  if(Input == "")
15293  {
15294  Utilities->CallLogPop(856);
15295  return;
15296  }
15297  while(Input[1] == ' ')
15298  {
15299  if(Input.Length() > 1)
15300  {
15301  Input = Input.SubString(2, Input.Length() - 1);
15302  }
15303  else
15304  {
15305  Input = "";
15306  Utilities->CallLogPop(857);
15307  return;
15308  }
15309  }
15310  if(Input == "")
15311  {
15312  Utilities->CallLogPop(858);
15313  return;
15314  }
15315  while(Input[Input.Length()] == ' ')
15316  {
15317  if(Input.Length() > 1)
15318  {
15319  Input = Input.SubString(1, Input.Length() - 1);
15320  }
15321  else
15322  {
15323  Input = "";
15324  Utilities->CallLogPop(859);
15325  return;
15326  }
15327  }
15328  // now strip spaces immediately after all commas and semicolons within the text
15329  AnsiString Output = "";
15330  bool DelimiterFound = false;
15331 
15332  for(int x = 1; x < Input.Length() + 1; x++)
15333  {
15334  if(DelimiterFound)
15335  {
15336  if(Input[x] == ' ')
15337  {
15338  continue;
15339  }
15340  }
15341  if((Input[x] != ',') && (Input[x] != ';'))
15342  {
15343  DelimiterFound = false;
15344  Output = Output + Input[x];
15345  }
15346  else
15347  {
15348  DelimiterFound = true;
15349  Output = Output + Input[x];
15350  }
15351  }
15352  if(Output == "")
15353  {
15354  Input = "";
15355  Utilities->CallLogPop(860);
15356  return;
15357  }
15358  // now strip spaces immediately before all commas and semicolons within the text
15359  Input = Output;
15360  Output = "";
15361  DelimiterFound = false;
15362  for(int x = Input.Length(); x > 0; x--)
15363  {
15364  if(DelimiterFound)
15365  {
15366  if(Input[x] == ' ')
15367  {
15368  continue;
15369  }
15370  }
15371  if((Input[x] != ',') && (Input[x] != ';'))
15372  {
15373  DelimiterFound = false;
15374  Output = AnsiString(Input[x]) + Output;
15375  }
15376  else
15377  {
15378  DelimiterFound = true;
15379  Output = AnsiString(Input[x]) + Output;
15380  }
15381  }
15382  Input = Output;
15383  Utilities->CallLogPop(861);
15384 }
15385 
15386 // ---------------------------------------------------------------------------
15387 
15388 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName) //this is the original version re-instated at v2.15.0 Beta6
15389 // checks if an Snt or Snt-sh entry with zero starting speed is followed (somewhere, not necessarily immediately) by a TimeLoc & has the same LocationName
15390 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
15391 // a signaller control entry & speed is zero or it is followed immediately by Frh, Fjo, Fns or F-nshs (allows empty stock pickup).
15392 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
15393 // are done in this function, they must be done elsewhere.
15394 //a starting speed > 0 always returns false
15395 {
15396  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
15397  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
15398  LocationName = "";
15399  if(TDEntry.StartSpeed > 0)
15400  {
15401  Utilities->CallLogPop(1784);
15402  return(false);
15403  }
15404  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
15405  {
15406  throw Exception("Error, first event not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
15407  }
15409  {
15410  Utilities->CallLogPop(852);
15411  return(false);
15412  }
15413  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
15414  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
15415 
15416  if(LocRear != "")
15417  {
15418  LocationName = LocRear;
15419  }
15420  else
15421  {
15422  LocationName = LocFront;
15423  }
15424  if(LocationName == "")
15425  {
15426  Utilities->CallLogPop(1036);
15427  return(false);
15428  }
15429  if(AVEntry0.SignallerControl)
15430  {
15431  Utilities->CallLogPop(1773);
15432  return(true);
15433  }
15434 // here if not a signaller start entry so must be at least one more entry, and it must be at the same location as the Snt to be located
15435 
15436 //Ok Not ok continue
15437 
15438 //Frh if Snt Frh-sh cdt
15439 //Fns if Snt Fns-sh fsp or rsp
15440 //Fjo if Snt TimeTimeLoc jbo
15441 //F-nshs if Snt pas dsc
15442 //TimeLoc dep Fer
15443 // TimeLoc arr
15444 
15445  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
15446  {
15447  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
15448  if(((AVEntry.Command == "Frh") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "F-nshs") || (AVEntry.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
15449  {
15450  Utilities->CallLogPop(1037);
15451  return(true);
15452  }
15453  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName == LocationName)) //will be a departure if same name- times not set yet so can't use them to confirm
15454  {
15455  Utilities->CallLogPop(2442);
15456  return(true);
15457  }
15458  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName != LocationName)) //arrival, not located
15459  {
15460  Utilities->CallLogPop(2438);
15461  return(false);
15462  }
15463  if((AVEntry.Command == "Fer") || (AVEntry.Command == "pas") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh") || (AVEntry.FormatType == TimeTimeLoc))
15464  {
15465  Utilities->CallLogPop(854);
15466  return(false);
15467  }
15468  if((AVEntry.Command == "cdt") || (AVEntry.Command == "dsc") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "jbo"))
15469  {
15470  continue;
15471  }
15472  }
15473  Utilities->CallLogPop(855);
15474  return(false);
15475 
15476 }
15477 
15478 // ---------------------------------------------------------------------------
15479 
15480 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
15481 {
15482  // checks that the new train start elements are valid - both exist & are connected, and that not
15483  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg) <--dropped at v2.18.0
15484  // & not starting with front on a continuation
15485  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
15486  int RearPosition = 0, FrontPosition = 0; // RearExitPos = 0; //dropped at v2.18.0
15487 
15488  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
15489  if(RearPosition < 0)
15490  // error message given in GetTrackVectorPositionFromString
15491  {
15492  Utilities->CallLogPop(759);
15493  return(false);
15494  }
15495  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
15496  if(FrontPosition < 0)
15497  // error message given in GetTrackVectorPositionFromString
15498  {
15499  Utilities->CallLogPop(760);
15500  return(false);
15501  }
15502  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
15503  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
15504 // TTrackType RearType = RearTrackElement.TrackType; //dropped at v2.18.0
15505  TTrackType FrontType = FrontTrackElement.TrackType;
15506 
15507  // check front & rear connected
15508  for(int x = 0; x < 4; x++)
15509  {
15510  if(RearTrackElement.Conn[x] == FrontPosition)
15511  {
15512 // RearExitPos = x; //dropped at v2.18.0
15513  break;
15514  }
15515  if(x == 3) //if it gets here & not already found then not connected
15516  {
15517  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
15518  Utilities->CallLogPop(762);
15519  return(false);
15520  }
15521  }
15522  // check not starting with front on a continuation
15523  if(FrontType == Continuation)
15524  {
15525  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
15526  Utilities->CallLogPop(937);
15527  return(false);
15528  }
15529  // check not starting on a level crossing
15530  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
15531  {
15532  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
15533  Utilities->CallLogPop(1951);
15534  return(false);
15535  }
15536  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
15537  {
15538  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
15539  Utilities->CallLogPop(1952);
15540  return(false);
15541  }
15542  // check if trying to start on diverging leg of points - allowed at v2.18.0, checks done during operation
15543 /*
15544  if((RearType == Points) && (RearExitPos == 3))
15545  {
15546  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
15547  Utilities->CallLogPop(936);
15548  return(false);
15549  }
15550 
15551  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
15552  {
15553  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
15554  Utilities->CallLogPop(1808);
15555  return(false);
15556  }
15557 */
15558  Utilities->CallLogPop(905);
15559  return(true);
15560 }
15561 
15562 // ---------------------------------------------------------------------------
15563 
15564 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
15565 // Rear & front element validity already checked in CheckStartPositionValidity
15566 // This checks for points in correct orientation, no train at start position and not starting on a locked route
15567 {
15568  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
15569  AnsiString(RearExitPos));
15570  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
15571 
15572  if(RearTrackElement.TrackType == Continuation)
15573  {
15574  EventType = FailTrainEntry;
15575  }
15576  else
15577  {
15578  EventType = FailCreateTrain;
15579  }
15580  int FrontPosition = RearTrackElement.Conn[RearExitPos];
15581  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
15582  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
15583  TTrackType RearType = RearTrackElement.TrackType;
15584  TTrackType FrontType = FrontTrackElement.TrackType;
15585  AnsiString RearName, FrontName;
15586 
15587  if(RearTrackElement.ActiveTrackElementName != "")
15588  {
15589  RearName = RearTrackElement.ActiveTrackElementName;
15590  }
15591  else
15592  {
15593  RearName = RearTrackElement.ElementID;
15594  }
15595  if(FrontTrackElement.ActiveTrackElementName != "")
15596  {
15597  FrontName = FrontTrackElement.ActiveTrackElementName;
15598  }
15599  else
15600  {
15601  FrontName = FrontTrackElement.ElementID;
15602  }
15603  TPrefDirElement PrefDirElement; // needed for next function but not used
15604  int LockedVectorNumber; // needed for next function but not used
15605 
15606  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
15607  {
15608  if(ReportFlag)
15609  {
15610  if(EventType == FailCreateTrain)
15611  {
15612  EventType = FailCreateLockedRoute;
15613  }
15614  else
15615  {
15616  EventType = FailEnterLockedRoute;
15617  }
15618  LogActionError(47, HeadCode, "", EventType, FrontName);
15619  }
15620  Utilities->CallLogPop(940);
15621  return(false);
15622  }
15623  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
15624  {
15625  if(ReportFlag)
15626  {
15627  if(EventType == FailCreateTrain)
15628  {
15629  EventType = FailCreateLockedRoute;
15630  }
15631  else
15632  {
15633  EventType = FailEnterLockedRoute;
15634  }
15635  LogActionError(48, HeadCode, "", EventType, RearName);
15636  }
15637  Utilities->CallLogPop(1809);
15638  return(false);
15639  }
15640  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
15641  {
15642  if(ReportFlag)
15643  {
15644  LogActionError(27, HeadCode, "", EventType, RearName);
15645  }
15646  Utilities->CallLogPop(1810);
15647  return(false);
15648  }
15649  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
15650  {
15651  if(ReportFlag)
15652  {
15653  if(EventType == FailCreateTrain)
15654  {
15655  LogActionError(28, HeadCode, "", EventType, FrontName);
15656  }
15657  else
15658  {
15659  LogActionError(43, HeadCode, "", EventType, RearName);
15660  }
15661  }
15662  Utilities->CallLogPop(941);
15663  return(false);
15664  }
15665  if(RearType == Bridge)
15666  {
15667  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
15668  {
15669  if(ReportFlag)
15670  {
15671  LogActionError(29, HeadCode, "", EventType, RearName);
15672  }
15673  Utilities->CallLogPop(942);
15674  return(false);
15675  }
15676  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
15677  {
15678  if(ReportFlag)
15679  {
15680  LogActionError(30, HeadCode, "", EventType, RearName);
15681  }
15682  Utilities->CallLogPop(943);
15683  return(false);
15684  }
15685  }
15686  if(FrontType == Bridge)
15687  {
15688  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
15689  {
15690  if(ReportFlag)
15691  {
15692  if(EventType == FailCreateTrain)
15693  {
15694  LogActionError(31, HeadCode, "", EventType, FrontName);
15695  }
15696  else
15697  {
15698  LogActionError(44, HeadCode, "", EventType, RearName);
15699  }
15700  }
15701  Utilities->CallLogPop(944);
15702  return(false);
15703  }
15704  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
15705  {
15706  if(ReportFlag)
15707  {
15708  if(EventType == FailCreateTrain)
15709  {
15710  LogActionError(45, HeadCode, "", EventType, FrontName);
15711  }
15712  else
15713  {
15714  LogActionError(46, HeadCode, "", EventType, RearName);
15715  }
15716  }
15717  Utilities->CallLogPop(945);
15718  return(false);
15719  }
15720  }
15721  EventType = FailCreatePoints; //modified at v2.18.0 so only fails if starting positions conflict with point attribute
15722  if(RearType == Points)
15723  {
15724  if(((RearTrackElement.Attribute == 1) && (RearExitPos == 1)) || ((RearTrackElement.Attribute == 0) && (RearExitPos == 3)))
15725  {
15726  if(ReportFlag)
15727  {
15728  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
15729  StopTTClockMessage(157, HeadCode + " can't be created, points set wrongly at " + RearName);
15730  }
15731  Utilities->CallLogPop(933);
15732  return(false);
15733  }
15734  }
15735  if(FrontType == Points)
15736  {
15737  if(((FrontTrackElement.Attribute == 1) && (FrontEntryPos == 1)) || ((FrontTrackElement.Attribute == 0) && (FrontEntryPos == 3)))
15738  {
15739  if(ReportFlag)
15740  {
15741  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
15742  StopTTClockMessage(158, HeadCode + " can't be created, points set wrongly at " + RearName);
15743  }
15744  Utilities->CallLogPop(934);
15745  return(false);
15746  }
15747  }
15748 
15749  //this section added at v2.9.1 to prevent entry for a train when there's a route set against it
15750  int HLoc = Track->TrackElementAt(1027, RearPosition).HLoc;
15751  int VLoc = Track->TrackElementAt(1028, RearPosition).VLoc;
15752  int ELink = Track->TrackElementAt(1029, RearPosition).Link[RearExitPos]; //if route entry corresponds to RearExitPos then it's set against the train
15753  int RouteNumber; //not used
15754  if(Track->TrackElementAt(1030, RearPosition).TrackType == Continuation)
15755  {
15756  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(8, HLoc, VLoc, ELink, RouteNumber))
15757  {
15758  EventType = FailEntryRouteSetAgainst;
15759  if(ReportFlag)
15760  {
15761  LogActionError(63, HeadCode, "", EventType, RearName);
15762  }
15763  Utilities->CallLogPop(2317);
15764  return(false);
15765  }
15766  }
15767  Utilities->CallLogPop(939);
15768  return(true);
15769 }
15770 
15771 // ---------------------------------------------------------------------------
15772 
15773 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
15774 {
15775  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
15776  "," + AnsiString(IncDigits));
15777  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
15778  {
15779  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
15780  }
15781  if(!Last2CharactersBothDigits(2, BaseHeadCode))
15782  {
15783  Utilities->CallLogPop(1893);
15784  return(BaseHeadCode);
15785  }
15786  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
15787  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
15788 
15789  while(NextRepeatDigits >= 100)
15790  {
15791  NextRepeatDigits -= 100; // rolls over after 99
15792  }
15793  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
15794 
15795  if(NextRepeatDigitsStr.Length() < 2)
15796  {
15797  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
15798  }
15799  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
15800 
15801  Utilities->CallLogPop(1365);
15802  return(NextRepeatHeadCode);
15803 }
15804 
15805 // ---------------------------------------------------------------------------
15806 
15807 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
15808 {
15809  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
15810  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
15811  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
15812  Utilities->CallLogPop(1366);
15813  return(NextRepeatTime);
15814 }
15815 
15816 // ---------------------------------------------------------------------------
15817 
15818 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
15819 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
15820 {
15821  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
15822  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
15823  int ForwardSecs = int(double(ForwardEventTime) * 86400);
15824  int ReverseSecs = int(double(ReverseEventTime) * 86400);
15825  int RepeatSecs = RepeatMinutes * 60;
15826 
15827  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
15828  {
15829  Utilities->CallLogPop(1367);
15830  return(false);
15831  }
15832  else
15833  {
15834  Utilities->CallLogPop(1368);
15835  return(true);
15836  }
15837 }
15838 
15839 // ---------------------------------------------------------------------------
15840 
15841 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool SetDataAndCheckLocations, bool GiveMessages)
15842 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent, set links if SetDataAndCheckLocations true
15843 
15844 /* Double crosslink (shuttle) table:
15845 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
15846  Code ShuttleLink- EntryPtr ShuttleLink-
15847  HeadCode EntryPtr
15848 
15849 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
15850 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
15851 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
15852 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
15853 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
15854 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
15855 
15856 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
15857 */
15858 
15859 {
15860  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
15861  NonRepeatingHeadCode);
15862  int ForwardCount = 0;
15863  int ReverseCount = 0;
15864  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
15865  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
15866  // Forward corresponds to Main, Reverse to Other
15867  TTrainDataEntry *MainTrainDataPtr = 0;
15868  TTrainDataEntry *OtherTrainDataPtr = 0;
15869 
15870  // forward check
15871  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15872  {
15873  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15874  if(TDEntry.HeadCode == MainHeadCode)
15875  {
15876  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15877  {
15878  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15879  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
15880  {
15881  MainTrainDataPtr = &TrainDataVector.at(x);
15882  ForwardEntryPtr = &AVEntry;
15883  ForwardCount++;
15884  ForwardTDVectorNumber = x;
15885  }
15886  }
15887  }
15888  }
15889  if(ForwardCount == 0)
15890  // this is an exception because the headcodes are selected in the same order as the forward check
15891  {
15892  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
15893  }
15894  if(ForwardCount > 1)
15895  {
15896  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
15897  MainHeadCode);
15898  TrainDataVector.clear();
15899  Utilities->CallLogPop(1061);
15900  return(false);
15901  }
15902  // reverse check
15903  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15904  {
15905  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15906  if(TDEntry.HeadCode == NonRepeatingHeadCode)
15907  {
15908  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15909  {
15910  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15911  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
15912  {
15913  OtherTrainDataPtr = &TrainDataVector.at(x);
15914  ReverseCount++;
15915  ReverseEntryPtr = &AVEntry;
15916  ReverseTDVectorNumber = x;
15917  }
15918  }
15919  }
15920  }
15921 
15922  if(ReverseCount == 0)
15923  {
15924  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
15925  TrainDataVector.clear();
15926  Utilities->CallLogPop(1062);
15927  return(false);
15928  }
15929  if(ReverseCount > 1)
15930  {
15931  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
15932  NonRepeatingHeadCode);
15933  TrainDataVector.clear();
15934  Utilities->CallLogPop(1063);
15935  return(false);
15936  }
15937  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
15938  {
15939  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
15940  TrainDataVector.clear();
15941  Utilities->CallLogPop(1064);
15942  return(false);
15943  }
15944  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
15945  {
15946  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
15947  TrainDataVector.clear();
15948  Utilities->CallLogPop(1065);
15949  return(false);
15950  }
15951  if(SetDataAndCheckLocations)
15952  {
15953  if(ForwardEntryPtr->LocationName == "")
15954  {
15955  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
15956  ". One or other service does not have a location set");
15957  TrainDataVector.clear();
15958  Utilities->CallLogPop(1066);
15959  return(false);
15960  }
15961  if(ReverseEntryPtr->LocationName == "")
15962  {
15963  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
15964  ". One or other service does not have a location set");
15965  TrainDataVector.clear();
15966  Utilities->CallLogPop(1067);
15967  return(false);
15968  }
15969  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
15970  {
15971  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
15972  " is at a different location to the referencing train " + MainHeadCode);
15973  TrainDataVector.clear();
15974  Utilities->CallLogPop(1068);
15975  return(false);
15976  }
15977  }
15978  if(ForwardEntryPtr->Command == "F-nshs")
15979  // i.e. the non repeating link into the shuttle service
15980  {
15981  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
15982  {
15983  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
15984  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
15985  TrainDataVector.clear();
15986  Utilities->CallLogPop(1069);
15987  return(false);
15988  }
15989  }
15990  if(ForwardEntryPtr->Command == "Fns-sh")
15991  // i.e. the non repeating link out from the shuttle service
15992  {
15993  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
15994  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
15995  {
15996  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
15997  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
15998  TrainDataVector.clear();
15999  Utilities->CallLogPop(1070);
16000  return(false);
16001  }
16002  }
16003  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
16004  // i.e. a non repeating link to or from the shuttle service
16005  {
16006  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
16007  {
16008  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
16009  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
16010  TrainDataVector.clear();
16011  Utilities->CallLogPop(1071);
16012  return(false);
16013  }
16014  }
16015 /* it's allowed to have a different description
16016  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
16017  {
16018  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
16019  {
16020  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
16021  TrainDataVector.clear();
16022  Utilities->CallLogPop(1072);
16023  return false;
16024  }
16025  }
16026 */
16027  if(ForwardEntryPtr->Command == "Sns-sh")
16028  {
16029  if(ReverseEntryPtr->Command != "F-nshs")
16030  {
16031  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
16032  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
16033  TrainDataVector.clear();
16034  Utilities->CallLogPop(1073);
16035  return(false);
16036  }
16037  }
16038  if(ForwardEntryPtr->Command == "F-nshs")
16039  {
16040  if(ReverseEntryPtr->Command != "Sns-sh")
16041  {
16042  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
16043  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
16044  TrainDataVector.clear();
16045  Utilities->CallLogPop(1074);
16046  return(false);
16047  }
16048  else
16049  {
16050  if(SetDataAndCheckLocations)
16051  {
16052  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
16053  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
16054  if(OtherTrainDataPtr->FixedDescription == "") //name changed at v2.16.1
16055  {
16056  OtherTrainDataPtr->FixedDescription = MainTrainDataPtr->FixedDescription;
16057  }
16058  }
16059  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
16060  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
16061  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
16062  }
16063  }
16064  if(ForwardEntryPtr->Command == "Sns-fsh")
16065  {
16066  if(ReverseEntryPtr->Command != "Fns-sh")
16067  {
16068  SecondPassMessage(GiveMessages,
16069  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
16070  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
16071  TrainDataVector.clear();
16072  Utilities->CallLogPop(1075);
16073  return(false);
16074  }
16075  }
16076  if(ForwardEntryPtr->Command == "Fns-sh")
16077  {
16078  if(ReverseEntryPtr->Command != "Sns-fsh")
16079  {
16080  SecondPassMessage(GiveMessages,
16081  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
16082  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
16083  TrainDataVector.clear();
16084  Utilities->CallLogPop(1076);
16085  return(false);
16086  }
16087  else
16088  {
16089  if(SetDataAndCheckLocations)
16090  {
16091  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
16092  // links to the non-repeating non-shuttle linked service
16093  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
16094  // needed for creating formatted timetable
16095  if(OtherTrainDataPtr->FixedDescription == "") //name changed at v2.16.1
16096  {
16097  OtherTrainDataPtr->FixedDescription = MainTrainDataPtr->FixedDescription;
16098  }
16099  }
16100  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
16101  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
16102  }
16103  }
16104  Utilities->CallLogPop(1077);
16105  return(true);
16106 }
16107 
16108 // ---------------------------------------------------------------------------
16109 
16110 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
16111 // Forward train is the finish shuttle entry 'Fns-sh'.
16112 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
16113 {
16114  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
16115  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
16116  int ForwardSecs = int(double(ForwardEventTime) * 86400);
16117  int ReverseSecs = int(double(ReverseEventTime) * 86400);
16118  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
16119 
16120  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
16121  {
16122  Utilities->CallLogPop(1369);
16123  return(false);
16124  }
16125  else
16126  {
16127  Utilities->CallLogPop(1370);
16128  return(true);
16129  }
16130 }
16131 
16132 // ---------------------------------------------------------------------------
16133 
16134 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
16135 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
16136 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
16137 // don't ever need to and as designed would skip repeats.
16138 
16139 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
16140 {
16141  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
16142  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
16143  {
16144  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
16145  }
16146  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
16147  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
16148  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
16149 
16150  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
16151  {
16152  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
16153  TrainDataVector.clear();
16154  Utilities->CallLogPop(1091);
16155  return(false);
16156  }
16157  while(LastActionCommand == "Fns")
16158  {
16159  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
16160  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
16161  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
16162  {
16163  SecondPassMessage(GiveMessages,
16164  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
16165  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
16166  TrainDataVector.clear();
16167  Utilities->CallLogPop(1092);
16168  return(false);
16169  }
16170  }
16171  // exit the 'while' with LastActionCommand FSH-XX
16172  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
16173  {
16174  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
16175  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
16176  ". The linking of two or more shuttles is not permitted.");
16177  TrainDataVector.clear();
16178  Utilities->CallLogPop(1093);
16179  return(false);
16180  }
16181  Utilities->CallLogPop(1094);
16182  return(true);
16183 }
16184 
16185 // ---------------------------------------------------------------------------
16186 
16187 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
16188 {
16189  if(!GiveMessages)
16190  {
16191  return;
16192  }
16193  // if(ServiceReference == "") ShowMessage(Message);
16194  if(!CheckHeadCodeValidity(12, false, ServiceReference))
16195  {
16196  ShowMessage(ServiceReference + "(not a valid service ref.): " + Message); //amended at v2.15.1 to give information on 'service' so can find it in lh list
16197  }
16198  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
16199  // false means don't give messages within the function
16200  else
16201  {
16202  ShowMessage("Service " + ServiceReference + ": " + Message);
16203  }
16204 }
16205 
16206 // ---------------------------------------------------------------------------
16207 
16208 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
16209 {
16210  if(!GiveMessages)
16211  {
16212  return;
16213  }
16214  ShowMessage(Message);
16215 }
16216 
16217 // ---------------------------------------------------------------------------
16218 
16219 AnsiString TTrainController::MinsToAnsiTime(int Input) //added at v2.15.0
16220 {
16221  TrainController->LogEvent("MinsToAnsiTime");
16222  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MinsToAnsiTime," + Input);
16223  int Mins = Input, Hrs = 0;
16224  while(Mins > 59)
16225  {
16226  Mins -= 60;
16227  Hrs++;
16228  }
16229  AnsiString AnsiMins = AnsiString(Mins);
16230  if(AnsiMins.Length() == 1)
16231  {
16232  AnsiMins = "0" + AnsiMins;
16233  }
16234  AnsiString AnsiHrs = AnsiString(Hrs);
16235  if(AnsiHrs.Length() == 1)
16236  {
16237  AnsiHrs = "0" + AnsiHrs;
16238  }
16239  Utilities->CallLogPop(2577);
16240  return(AnsiHrs + ':' + AnsiMins);
16241 }
16242 
16243 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
16244 // ---------------------------------------------------------------------------
16245 
16246 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
16247 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
16248 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
16249 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
16250 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
16251 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set against start position 57-N5
16252 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
16253 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
16254 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
16255 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
16256 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
16257 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road, please move it if possible
16258 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
16259 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
16260 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
16261 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
16262 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
16263 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
16264 // FailMissedDSC: 06:00:10: ERROR: 2F43 failed to change its description at Essex Road
16265 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
16266 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
16267 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
16268 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
16269 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
16270 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
16271 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
16272 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
16273 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
16274 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
16275 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
16276 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
16277 // FailEntryRouteSetAgainst: 06:00:10: WARNING: 2F43 can't enter railway, route set against it at entry position 57-N5 //added at v2.9.1
16278 {
16279  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
16280  AnsiString(ActionEventType) + "," + LocationID);
16281  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
16282 
16283  TDateTime ActualTime = TrainController->TTClockTime; //moved from lower down at v2.9.1
16284  AnsiString TimeAndHeadCode = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode; //added at v2.9.1 to give more info to user
16285 
16286  Prefix = " ERROR: ";
16287  if(ActionEventType == FailTrainEntry)
16288  {
16289  Prefix = " HELD: ";
16290  ErrorLog = " can't enter railway, train obstructing entry position ";
16291  WarningStr = " can't enter railway, train obstructing entry position ";
16292  Display->WarningLog(1, TimeAndHeadCode + WarningStr + LocationID);
16293  }
16294  else if(ActionEventType == FailEntryRouteSetAgainst) //added at v2.9.1
16295  {
16296  Prefix = " HELD: ";
16297  ErrorLog = " can't enter railway, route set against it at entry position ";
16298  WarningStr = " can't enter railway, route set against it at entry position ";
16299  Display->WarningLog(10, TimeAndHeadCode + WarningStr + LocationID);
16300  }
16301  else if(ActionEventType == FailCreateTrain)
16302  {
16303  Prefix = " HELD: ";
16304  ErrorLog = " can't be created, train obstructing ";
16305  WarningStr = " can't be created, train obstructing ";
16306  Display->WarningLog(2, TimeAndHeadCode + WarningStr + LocationID);
16307  }
16308  else if(ActionEventType == FailCreateLockedRoute)
16309  {
16310  Prefix = " HELD: ";
16311  ErrorLog = " can't be created on a locked route at ";
16312  WarningStr = " can't be created on a locked route at ";
16313  Display->WarningLog(4, TimeAndHeadCode + WarningStr + LocationID);
16314  }
16315  else if(ActionEventType == FailEnterLockedRoute)
16316  {
16317  Prefix = " HELD: ";
16318  ErrorLog = " can't enter on a locked route at ";
16319  WarningStr = " can't enter on a locked route at ";
16320  Display->WarningLog(5, TimeAndHeadCode + WarningStr + LocationID);
16321  }
16322  else if(ActionEventType == FailCreatePoints)
16323  {
16324  Prefix = " HELD: ";
16325  ErrorLog = " can't be created, points set wrongly at ";
16326  WarningStr = " can't be created, points set wrongly at ";
16327  Display->WarningLog(3, TimeAndHeadCode + WarningStr + LocationID);
16328  }
16329  else if(ActionEventType == FailUnexpectedExitRailway)
16330  {
16331  ErrorLog = " left railway unexpectedly at ";
16332  UnexpectedExits++;
16333  }
16334  else if(ActionEventType == FailIncorrectExit)
16335  {
16336  ErrorLog = " left railway at an incorrect exit at ";
16337  IncorrectExits++;
16338  }
16339  else if(ActionEventType == FailLocTooShort)
16340  {
16341  ErrorLog = " failed to split - location too short at ";
16342  WarningStr = " failed to split, location too short at ";
16343  Display->WarningLog(6, TimeAndHeadCode + WarningStr + LocationID);
16344  }
16345  else if(ActionEventType == FailSplitDueToOtherTrain)
16346  {
16347  Prefix = " HELD: ";
16348  ErrorLog = " unable to split - other train obstructing at ";
16349  WarningStr = " unable to split - other train obstructing at ";
16350  Display->WarningLog(7, TimeAndHeadCode + WarningStr + LocationID);
16351  }
16352  else if(ActionEventType == FailUnexpectedBuffers)
16353  {
16354  ErrorLog = " stopped at buffers unexpectedly at position ";
16355  }
16356  else if(ActionEventType == FailMissedArrival)
16357  {
16358  ErrorLog = " failed to stop at ";
16359  MissedStops++;
16360  }
16361  else if(ActionEventType == FailMissedSplit)
16362  {
16363  ErrorLog = " failed to split at ";
16365  }
16366  else if(ActionEventType == FailMissedJBO)
16367  {
16368  ErrorLog = " failed to be joined by other train at ";
16370  }
16371  else if(ActionEventType == FailMissedDSC) //new at v2.15.0
16372  {
16373  ErrorLog = " failed to change its description at ";
16374 // OtherMissedEvents++; shouldn't count
16375  }
16376  else if(ActionEventType == FailMissedJoinOther)
16377  {
16378  ErrorLog = " failed to join other train at ";
16380  }
16381  else if(ActionEventType == FailMissedTerminate)
16382  {
16383  ErrorLog = " failed to terminate at ";
16385  }
16386  else if(ActionEventType == FailMissedNewService)
16387  {
16388  ErrorLog = " failed to form new service at ";
16390  }
16391  else if(ActionEventType == FailMissedExitRailway)
16392  {
16393  ErrorLog = " failed to exit railway ";
16395  }
16396  else if(ActionEventType == FailMissedChangeDirection)
16397  {
16398  ErrorLog = " failed to change direction at ";
16399 // OtherMissedEvents++; //dropped at v2.12.0 as cdt shouldn't count
16400  }
16401  else if(ActionEventType == FailMissedPass)
16402  {
16403  ErrorLog = " failed to pass ";
16404 // OtherMissedEvents++; //dropped at v2.12.0 as missed pass shouldn't count
16405  }
16406  else if(ActionEventType == FailBuffersPreventingStart)
16407  {
16408  ErrorLog = " facing buffers and unable to start at ";
16409  }
16410  else if(ActionEventType == FailDerailed)
16411  {
16412  ErrorLog = " DERAILED at position ";
16413  Prefix = " DERAILMENT: ";
16414  Derailments++;
16415  }
16416  else if(ActionEventType == FailBufferCrash)
16417  {
16418  ErrorLog = " CRASHED INTO BUFFERS at ";
16419  Prefix = " CRASH: ";
16420  CrashedTrains++;
16421  }
16422  else if(ActionEventType == FailLevelCrossingCrash)
16423  {
16424  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
16425  Prefix = " CRASH: ";
16426  CrashedTrains++;
16427  }
16428  else if(ActionEventType == FailCrashed)
16429  {
16430  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
16431  Prefix = " CRASH: ";
16432  CrashedTrains++;
16433  CrashedTrains++;
16434  }
16435  else if(ActionEventType == FailSPAD)
16436  {
16437  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
16438  Prefix = " SPAD: ";
16439  SPADEvents++;
16440  }
16441  else if(ActionEventType == FailLockedRoute)
16442  {
16443  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
16444  Prefix = " SPAD RISK: ";
16445  SPADRisks++;
16446  }
16447  else if(ActionEventType == RouteForceCancelled)
16448  {
16449  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
16450  }
16451  else if(ActionEventType == WaitingForJBO)
16452  {
16453  Prefix = " WARNING: ";
16454  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
16455  WarningStr = " waiting to join " + OtherHeadCode + " at ";
16456  Display->WarningLog(8, TimeAndHeadCode + WarningStr + LocationID);
16457  }
16458  else if(ActionEventType == WaitingForFJO)
16459  {
16460  Prefix = " WARNING: ";
16461  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
16462  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
16463  Display->WarningLog(9, TimeAndHeadCode + WarningStr + LocationID);
16464  }
16465 
16466  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
16467  PerfLogForm->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
16468  Utilities->CallLogPop(1371);
16469 }
16470 
16471 // ---------------------------------------------------------------------------
16472 
16474 {
16475 /* //for testing purposes
16476  TrainDataEntry
16477  AnsiString HeadCode, Description;//null on creation
16478  int StartSpeed, MaxRunningSpeed;//both kph
16479  int RepeatNumber;
16480  TActionVector ActionVector;
16481  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
16482  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
16483 
16484  ActionVectorEntry
16485  TTimetableEntryType FormatType;
16486  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
16487  AnsiString LocationName, Command, OtherHeadCode;//null on creation
16488  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
16489  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
16490  int RepeatNumber;
16491 
16492  TrainOperatingData
16493  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
16494  int TrainID;
16495  TRunningEntry RunningEntry;
16496  TDateTime StartTime;
16497  AnsiString HeadCode;
16498 */
16499  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
16500  std::ofstream OutFile("TrainData.csv");
16501 
16502  if(OutFile == 0)
16503  {
16504  ShowMessage("Output file TrainData.csv failed to open");
16505  Utilities->CallLogPop(1372);
16506  return;
16507  }
16508  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16509  {
16510  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
16511  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
16512 
16513  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.FixedDescription.c_str() /* name changed at v2.16.1*/
16514  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
16515 
16516  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
16517  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
16518  "RepeatNumber" << '\n' << '\n';
16519  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
16520  {
16521  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
16522  AnsiString TimetableEntryTypeStr;
16523  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
16524  switch(AVEntry.FormatType)
16525  {
16526  case 0:
16527  {
16528  TimetableEntryTypeStr = "NoFormat";
16529  break;
16530  }
16531 
16532  case 1:
16533  {
16534  TimetableEntryTypeStr = "TimeLoc";
16535  break;
16536  }
16537 
16538  case 2:
16539  {
16540  TimetableEntryTypeStr = "TimeTimeLoc";
16541  break;
16542  }
16543 
16544  case 3:
16545  {
16546  TimetableEntryTypeStr = "TimeCmd";
16547  break;
16548  }
16549 
16550  case 4:
16551  {
16552  TimetableEntryTypeStr = "StartNew";
16553  break;
16554  }
16555 
16556  case 5:
16557  {
16558  TimetableEntryTypeStr = "TimeCmdHeadCode";
16559  break;
16560  }
16561 
16562  case 6:
16563  {
16564  TimetableEntryTypeStr = "FinRemHere";
16565  break;
16566  }
16567 
16568  case 7:
16569  {
16570  TimetableEntryTypeStr = "FNSShuttle";
16571  break;
16572  }
16573 
16574  case 8:
16575  {
16576  TimetableEntryTypeStr = "SNTShuttle";
16577  break;
16578  }
16579 
16580  case 9:
16581  {
16582  TimetableEntryTypeStr = "SNSShuttle";
16583  break;
16584  }
16585 
16586  case 10:
16587  {
16588  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
16589  break;
16590  }
16591 
16592  case 11:
16593  {
16594  TimetableEntryTypeStr = "FSHNewService";
16595  break;
16596  }
16597 
16598  case 12:
16599  {
16600  TimetableEntryTypeStr = "Repeat";
16601  break;
16602  }
16603 
16604  default:
16605  {
16606  TimetableEntryTypeStr = "Default";
16607  break;
16608  }
16609  }
16610  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
16611  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
16612  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
16613  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
16614  AVEntry.NumberOfRepeats << '\n';
16615  }
16616  OutFile << '\n';
16617  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
16618  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
16619  {
16620  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
16621  AnsiString RunningEntryStr;
16622  // NotStarted, Running, Exited
16623  switch(TOD.RunningEntry)
16624  {
16625  case 0:
16626  {
16627  RunningEntryStr = "NotStarted";
16628  break;
16629  }
16630 
16631  case 1:
16632  {
16633  RunningEntryStr = "Running";
16634  break;
16635  }
16636 
16637  case 2:
16638  {
16639  RunningEntryStr = "Exited";
16640  break;
16641  }
16642  }
16643  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
16644  }
16645  OutFile << '\n';
16646  }
16647  OutFile.close();
16648  Utilities->CallLogPop(1373);
16649 }
16650 
16651 // ---------------------------------------------------------------------------
16652 
16653 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
16654 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
16655 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed (in ClockTimer2 when StopTTClockFlag is false)
16656 {
16657  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
16658  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
16660  ShowMessage(Message);
16661  BaseTime = TDateTime::CurrentDateTime();
16662  StopTTClockFlag = false;
16663  Utilities->CallLogPop(1374);
16664 }
16665 
16666 // ---------------------------------------------------------------------------
16667 
16668 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
16669 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
16670 // from the start of the relevant vectors. Can't save the pointer values
16671 // as these will be different each time the vectors are created
16672 {
16673  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
16674  Utilities->SaveFileInt(SessionFile, TrainVector.size());
16675  for(unsigned int x = 0; x < TrainVector.size(); x++)
16676  {
16677  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
16678  }
16679  Utilities->CallLogPop(1375);
16680 }
16681 
16682 // ---------------------------------------------------------------------------
16683 
16684 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
16685 {
16686  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
16687  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
16688  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
16689  // by zero error in calculating AValue, use 1
16690  for(int x = 0; x < NumberOfTrains; x++)
16691  {
16692  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
16693  // by zero error in calculating AValue, use 1
16694  NewTrain->LoadOneSessionTrain(0, SessionFile);
16695  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
16696  // added at v2.4.0. have to include as that value not stored in session file
16697  {
16698  NewTrain->StoppedWithoutPower = true;
16699  }
16700  TrainVector.push_back(*NewTrain);
16701  LastTrainLoaded = x;
16702  }
16703  delete NewTrain;
16704  Utilities->CallLogPop(1376);
16705 }
16706 
16707 // ---------------------------------------------------------------------------
16708 
16709 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
16710 {
16711  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
16712  int NumberOfTrains;
16713 
16714  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
16715  {
16716  Utilities->CallLogPop(1377);
16717  return(false);
16718  }
16719  for(int x = 0; x < NumberOfTrains; x++)
16720  {
16721  if(!(TTrain::CheckOneSessionTrain(InFile)))
16722  {
16723  Utilities->CallLogPop(1378);
16724  return(false);
16725  }
16726  }
16727  Utilities->CallLogPop(1379);
16728  return(true);
16729 }
16730 
16731 // ---------------------------------------------------------------------------
16732 
16733 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
16734 {
16735  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
16736  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
16737  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
16738  {
16739  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
16740  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RearTrackVectorPosition);
16741  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
16742  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
16743  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
16744  }
16745  Utilities->CallLogPop(1380);
16746 }
16747 
16748 // ---------------------------------------------------------------------------
16749 
16750 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
16751 {
16752  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
16753  TAllRoutes::TLockedRouteClass LockedRouteObject;
16754  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
16755 
16756  for(int x = 0; x < LockedRouteVectorSize; x++)
16757  {
16758  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
16759  LockedRouteObject.RearTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
16760  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
16761  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
16762  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
16763  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
16764  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
16765  }
16766  Utilities->CallLogPop(1381);
16767 }
16768 
16769 // ---------------------------------------------------------------------------
16770 
16771 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
16772 {
16773  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
16774  int LockedRouteVectorSize;
16775 
16776  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
16777  {
16778  Utilities->CallLogPop(1382);
16779  return(false);
16780  }
16781  for(int x = 0; x < LockedRouteVectorSize; x++)
16782  {
16783  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
16784  {
16785  Utilities->CallLogPop(1383);
16786  return(false);
16787  }
16788  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
16789  {
16790  Utilities->CallLogPop(1384);
16791  return(false);
16792  }
16793  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
16794  {
16795  Utilities->CallLogPop(1385);
16796  return(false);
16797  }
16798  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
16799  {
16800  Utilities->CallLogPop(1386);
16801  return(false);
16802  }
16803  if(!Utilities->CheckFileDouble(SessionFile))
16804  {
16805  Utilities->CallLogPop(1387);
16806  return(false);
16807  }
16808  }
16809  Utilities->CallLogPop(1388);
16810  return(true);
16811 }
16812 
16813 // ---------------------------------------------------------------------------
16814 
16815 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
16816 {
16817  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
16818  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
16819  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
16820  {
16821  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
16822  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
16823  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
16824  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
16825  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
16826  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
16827  }
16828  Utilities->CallLogPop(1389);
16829 }
16830 
16831 // ---------------------------------------------------------------------------
16832 
16833 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
16834 {
16835  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
16836  TContinuationAutoSigEntry ContinuationAutoSigObject;
16837  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
16838 
16839  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
16840  {
16841  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
16842  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
16843  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
16844  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
16845  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
16846  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
16847  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
16848  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
16849  }
16850  Utilities->CallLogPop(1390);
16851 }
16852 
16853 // ---------------------------------------------------------------------------
16854 
16855 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
16856 {
16857  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
16858  int ContinuationAutoSigVectorSize;
16859 
16860  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
16861  {
16862  Utilities->CallLogPop(1391);
16863  return(false);
16864  }
16865  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
16866  {
16867  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
16868  {
16869  Utilities->CallLogPop(1392);
16870  return(false);
16871  }
16872  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
16873  {
16874  Utilities->CallLogPop(1393);
16875  return(false);
16876  }
16877  if(!Utilities->CheckFileDouble(SessionFile))
16878  {
16879  Utilities->CallLogPop(1405);
16880  return(false);
16881  }
16882  if(!Utilities->CheckFileDouble(SessionFile))
16883  {
16884  Utilities->CallLogPop(1406);
16885  return(false);
16886  }
16887  if(!Utilities->CheckFileDouble(SessionFile))
16888  {
16889  Utilities->CallLogPop(1407);
16890  return(false);
16891  }
16892  if(!Utilities->CheckFileDouble(SessionFile))
16893  {
16894  Utilities->CallLogPop(1394);
16895  return(false);
16896  }
16897  }
16898  Utilities->CallLogPop(1395);
16899  return(true);
16900 }
16901 
16902 // ---------------------------------------------------------------------------
16903 
16904 /*
16905  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
16906  {
16907  public:
16908  AnsiString Description; ///< service description
16909  AnsiString HeadCode; ///< service headcode
16910  int RepeatNumber; ///< service RepeatNumber
16911  int IncrementalMinutes; ///< Repeat separation in minutes
16912  int IncrementalDigits; ///< Repeat headcode separation
16913  int VectorPosition; ///< TrackVectorPosition for the continuation element
16914  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
16915  };
16916 
16917 
16918  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
16919  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
16920 */
16921 
16923 // build this into timetable load so session loading can use it too
16924 // being a multimap it automatically sorts in ascending EventTime order
16925 {
16926  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
16928  // need to clear as this called twice when load a session
16929  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16930  {
16931  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
16932  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
16933  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
16934 
16935  if(AVFirstEntry.Command == "Snt")
16936  // new train (no need to include Snt-sh since they can't start at a continuation)
16937  {
16940  {
16942  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
16943  // retains this value for all repeats
16944  CTEEntry.RepeatNumber = 0; // for first entry
16945  CTEEntry.TrainDataEntryPtr = &TDEntry;
16946  // retains this value for all repeats
16947  CTEEntry.HeadCode = TDEntry.HeadCode;
16948  CTEEntry.FixedDescription = TDEntry.FixedDescription; //name changed at v2.16.1
16949  CTEEntry.IncrementalMinutes = 0;
16950  CTEEntry.IncrementalDigits = 0;
16951  if(AVLastEntry.FormatType == Repeat)
16952  {
16953  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
16954  // retains this value or 0 for all repeats
16955  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
16956  // retains this value or 0 for all repeats
16957  }
16958  CTEMMP.first = AVFirstEntry.EventTime;
16959  CTEMMP.second = CTEEntry;
16960  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
16961  // base entry
16962  if(TDEntry.NumberOfTrains > 1)
16963  {
16964  if(AVLastEntry.FormatType != Repeat)
16965  {
16966  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
16967  }
16968  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
16969  {
16970  CTEEntry.RepeatNumber = y;
16971  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
16972  // CTEEntry.VectorPosition stays same
16973  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
16974  CTEMMP.second = CTEEntry;
16975  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
16976  }
16977  }
16978  }
16979  }
16980  }
16981  Utilities->CallLogPop(1396);
16982 }
16983 
16984 // ---------------------------------------------------------------------------
16985 
16987 {
16988  // called when WarningFlashCount == 0 or when press zoomout button
16989  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
16990  if(!Display->ZoomOutFlag)
16991  {
16992  Utilities->CallLogPop(1156);
16993  return;
16994  }
16995  for(unsigned int x = 0; x < TrainVector.size(); x++)
16996  {
16997  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
16998  // if OldPlotElement[x] == -1 then ignore (not plotted)
17000  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
17001  }
17002  Display->Update();
17003  // need to keep this since Update() not called for PlotSmallOutput as too slow
17004  Utilities->CallLogPop(742);
17005 }
17006 
17007 // ---------------------------------------------------------------------------
17008 
17009 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
17010 {
17011  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
17012  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
17013  {
17014  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
17015  }
17016  Utilities->CallLogPop(740);
17017  return(TrainVector.at(VecPos));
17018 }
17019 
17020 // ---------------------------------------------------------------------------
17021 
17022 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
17023 {
17024  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
17025  AnsiString RetStr = "", PartStr = "";
17026 
17027 
17028 /*
17029  Have description & mass etc for train at top - header, then array of actions
17030 
17031  class TActionVectorEntry
17032  {
17033  public:
17034  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
17036  bool SignallerControl;
17038  bool Warning;
17040  int NumberOfRepeats;
17042  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
17044  TDateTime EventTime, ArrivalTime, DepartureTime;
17046  TNumList ExitList;
17048  TTimetableFormatType FormatType;
17050  TTimetableLocationType LocationType;
17052  TTimetableSequenceType SequenceType;
17054  TTimetableShuttleLinkType ShuttleLinkType;
17056  TTrainDataEntry *LinkedTrainEntryPtr;
17058  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
17060 
17061  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
17062 
17063  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
17064 
17065  class TTrainOperatingData
17066  {
17067  public:
17068  int TrainID;
17069  TActionEventType EventReported;
17070  TRunningEntry RunningEntry;
17071 
17072  //inline function
17073  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
17074  };
17075 
17076  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
17077 
17078  class TTrainDataEntry
17079  {
17080  public:
17081  AnsiString HeadCode, ServiceReference, Description;
17083  double MaxBrakeRate;
17085  double MaxRunningSpeed;
17087  double PowerAtRail;
17089  int Mass;
17091  int NumberOfTrains;
17093  int SignallerSpeed;
17095  int StartSpeed;
17097  TActionVector ActionVector;
17099  TTrainOperatingDataVector TrainOperatingDataVector;
17101 
17102  //inline function
17103  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
17104  };
17105 
17106  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
17107 
17108  //formatted timetable types
17109  class TOneTrainFormattedEntry
17110  {
17111  AnsiString Action;//includes location if relevanr
17112  AnsiString Time;
17113  };
17114 
17115  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
17116 
17117  class TOneCompleteFormattedTrain//headcode + list of actions
17118  {
17119  public:
17120  AnsiString HeadCode;
17121  TOneFormattedTrainVector OneFormattedTrainVector;
17122  };
17123 
17124  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
17125 
17126  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
17127  {
17128  public:
17129  AnsiString Header;//description, mass, power, brake rate etc
17130  int NumberOfTrains;// number of repeats + 1
17131  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
17132  };
17133 
17134 
17135  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
17136  //end of formatted timetable types
17137 
17138 */
17139 
17140  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17141 
17142  // format "16/06/2009 20:55:17"
17143  // avoid characters in filename:= / \ : * ? " < > |
17144  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
17145 
17146  AnsiString ShortTTName = "";
17147 
17148  for(int x = TTFileName.Length(); x > 0; x--)
17149  {
17150  if(TTFileName[x] == '\\')
17151  {
17152  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
17153  break;
17154  }
17155  }
17156 
17157  ShowMessage("Creates two timetables named " + ShortTTName +
17158  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
17159 
17160  Screen->Cursor = TCursor(-11); // Hourglass
17161 
17162  AnsiString FormatNoDPStr = "#######0";
17163  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
17164 
17166  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
17167  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
17168 
17169  // all timetable in formatted form
17170  //create the AllTTTrains vector
17171  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
17172  {
17173  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
17174  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
17175  if(TrainDataEntry.Mass > 0)
17176  {
17177  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
17178  }
17179  if(TrainDataEntry.PowerAtRail > 0)
17180  {
17181  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
17182  }
17183  if(TrainDataEntry.MaxBrakeRate > 0)
17184  {
17185  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
17186  }
17187  if(TrainDataEntry.MaxRunningSpeed > 0)
17188  {
17189  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
17190  }
17191  FirstHeadCode = TrainDataEntry.HeadCode;
17192  int IncDigits = 0, IncMinutes = 0;
17193  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
17194  if(!ActionVector.empty())
17195  {
17196  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
17197  {
17198  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
17199  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
17200  }
17201  }
17202  TTrainFormattedInformation OneTTLine;
17203  // contains all information for a single TT entry (including repeats)
17204  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
17205  {
17206  OneTTLine.Header = "";
17207  if((TrainDataEntry.FixedDescription != "") && (MassStr != "")) //name changed at v2.16.1
17208  {
17209  OneTTLine.Header = TrainDataEntry.FixedDescription + MassStr + PowerStr + BrakeStr + MaxSpeedStr; //name changed at v2.16.1
17210  }
17211  else if(TrainDataEntry.FixedDescription != "") //name changed at v2.16.1
17212  {
17213  OneTTLine.Header = TrainDataEntry.FixedDescription; //name changed at v2.16.1
17214  }
17215  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
17216  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
17217  for(unsigned int z = 0; z < ActionVector.size(); z++)
17218  {
17219  TOneTrainFormattedEntry OneTTEntry;
17220  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
17221  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
17222  AnsiString PartStr = "", TimeStr = "";
17223 /*
17224  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
17225  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
17226  ExitRailway};
17227  enum TTimetableSequenceType {NoSequence, StartSequence, FinishSequence, IntermediateSequence, SequTypeForRepeatEntry};
17228  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
17229  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
17230 */
17231  if(ActionVectorEntry.SequenceType == StartSequence)
17232  {
17233  if(ActionVectorEntry.FormatType == StartNew)
17234  {
17235  if(ActionVectorEntry.LocationName != "")
17236  {
17237  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
17238  {
17239  PartStr = "Enters at " + ActionVectorEntry.LocationName;
17240  }
17241  else
17242  {
17243  PartStr = "Created at " + ActionVectorEntry.LocationName;
17244  }
17245  }
17246  else // may be a named continuation or other element, and if so report that
17247  {
17248  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
17249  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
17250  {
17251  if(LocName != "")
17252  {
17253  PartStr = "Enters at " + LocName;
17254  }
17255  else // use rear position if it's a continuation
17256  {
17257  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
17258  }
17259  }
17260  else // not a continuation
17261  {
17262  if(LocName != "")
17263  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
17264  // but include anyway
17265  {
17266  PartStr = "Created at " + LocName;
17267  }
17268  else // use rear position again
17269  {
17270  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
17271  }
17272  }
17273  }
17274  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
17275  }
17276  else if(ActionVectorEntry.FormatType == SNTShuttle)
17277  {
17278  if(y == 0) // first train
17279  {
17280  PartStr = "Enters at " + ActionVectorEntry.LocationName;
17281  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
17282  }
17283  else
17284  {
17285  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
17286  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
17287  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
17288  } // y-1 for headcode above since it is the last repeat value that the train is from
17289 
17290  }
17291  else if(ActionVectorEntry.Command == "Sfs")
17292  {
17293  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
17294  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17295  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
17296  }
17297  else if(ActionVectorEntry.Command == "Sns")
17298  {
17299  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
17300  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17301  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
17302  }
17303  else if(ActionVectorEntry.FormatType == SNSShuttle)
17304  {
17305  if(y == 0) // first entry from shuttle
17306  {
17307  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
17308  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
17309  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
17310  }
17311  else
17312  {
17313  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
17314  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
17315  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
17316  } // y-1 for headcode above since it is the last repeat value that the train is from
17317 
17318  }
17319  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
17320  {
17321  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
17322  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
17323  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
17324  AnsiString FirstHeadCode = TDE->HeadCode;
17325  int LastRepeatNumber = TDE->NumberOfTrains - 1;
17326  // a shuttle has to have at least 1 repeat
17327  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
17328  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
17329  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
17330  }
17331  }
17332  else if(ActionVectorEntry.SequenceType == IntermediateSequence)
17333  {
17334  if(ActionVectorEntry.FormatType == TimeTimeLoc)
17335  {
17336  // here need 2 entries if times different so push the first right away & the second later
17337  // if times same just give the arrival entry
17338  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
17339  {
17340  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
17341  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
17342  OneTTEntry.Action = PartStr;
17343  OneTTEntry.Time = TimeStr;
17344  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
17345  PartStr = "Departs from " + ActionVectorEntry.LocationName;
17346  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
17347  }
17348  else
17349  {
17350  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
17351  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
17352  }
17353  }
17354  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
17355  {
17356  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
17357  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
17358  }
17359  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
17360  {
17361  PartStr = "Departs from " + ActionVectorEntry.LocationName;
17362  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
17363  }
17364  else if(ActionVectorEntry.FormatType == PassTime)
17365  {
17366  PartStr = "Passes " + ActionVectorEntry.LocationName;
17367  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
17368  }
17369  else if(ActionVectorEntry.Command == "jbo")
17370  {
17371  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
17372  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17373  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
17374  }
17375  else if(ActionVectorEntry.Command == "fsp")
17376  {
17377  if(ActionVectorEntry.SplitDistribution != "") //new at v2.15.0
17378  {
17379  PartStr = "Splits from front [mass%-power% = " + ActionVectorEntry.SplitDistribution + "] at " + ActionVectorEntry.LocationName + " to form";
17380  }
17381  else
17382  {
17383  PartStr = "Splits from front [mass%-power% = 50-50] at " + ActionVectorEntry.LocationName + " to form";
17384  }
17385  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17386  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
17387  }
17388  else if(ActionVectorEntry.Command == "rsp")
17389  {
17390  if(ActionVectorEntry.SplitDistribution != "") //new at v2.15.0
17391  {
17392  PartStr = "Splits from front [mass%-power% = " + ActionVectorEntry.SplitDistribution + "] at " + ActionVectorEntry.LocationName + " to form";
17393  }
17394  else
17395  {
17396  PartStr = "Splits from front [mass%-power% = 50-50] at " + ActionVectorEntry.LocationName + " to form";
17397  }
17398  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17399  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
17400  }
17401  else if(ActionVectorEntry.Command == "cdt")
17402  {
17403  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
17404  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
17405  }
17406  else if(ActionVectorEntry.Command == "dsc")
17407  {
17408  PartStr = "Changes description at " + ActionVectorEntry.LocationName;
17409  TimeStr = Utilities->Format96HHMM(GetRepeatTime(76, ActionVectorEntry.EventTime, y, IncMinutes));
17410  }
17411  }
17412  else if(ActionVectorEntry.SequenceType == FinishSequence)
17413  {
17414  if(ActionVectorEntry.Command == "Fns")
17415  {
17416  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17417  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17418  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
17419  }
17420  else if(ActionVectorEntry.Command == "F-nshs")
17421  {
17422  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17423  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
17424  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
17425  }
17426  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
17427  {
17428  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
17429  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
17430  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
17431  // y+1 because it's the NEXT service repeat number that is relevant
17432  }
17433  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
17434  {
17435  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17436  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
17437  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
17438  }
17439  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
17440  {
17441  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17442  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
17443  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
17444  // y+1 because it's the NEXT service repeat number that is relevant
17445  }
17446  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
17447  {
17448  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
17449  // only used in chronological tt
17450  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
17451  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
17452  }
17453  else if(ActionVectorEntry.Command == "Frh")
17454  {
17455  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
17456  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
17457  if(z > 0)
17458  // should be for finish entry but include check for safety
17459  {
17460  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
17461  {
17462  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
17463  }
17464  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
17465  {
17466  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
17467  }
17468  else
17469  {
17470  TimeStr = " "; // shouldn't ever get here
17471  }
17472  }
17473  }
17474  else if(ActionVectorEntry.Command == "Fer")
17475  {
17476  AnsiString AllowedExits;
17477  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
17478  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
17479  }
17480  else if(ActionVectorEntry.Command == "Fjo")
17481  {
17482  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
17483  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17484  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
17485  }
17486  }
17487  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
17488  {
17489  continue; // no entry needed for a repeat
17490  }
17491  OneTTEntry.Action = PartStr;
17492  OneTTEntry.Time = TimeStr;
17493  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
17494  // one per action
17495  }
17496  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
17497  // one per repeat
17498  }
17499  AllTTTrains->push_back(OneTTLine); // one per repeating train
17500  }
17501  // AllTTTrains vector now complete
17502 
17503  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
17504 
17505  if(TTFile == 0)
17506  {
17507  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
17508  delete AllTTTrains;
17509  Utilities->CallLogPop(1567);
17510  return;
17511  }
17512 /* formatted timetable types
17513  class TOneTrainFormattedEntry
17514  {
17515  AnsiString Action;//includes location if relevant
17516  AnsiString Time;
17517  };
17518 
17519  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
17520 
17521  class TOneCompleteFormattedTrain//headcode + list of actions
17522  {
17523  public:
17524  AnsiString HeadCode;
17525  TOneFormattedTrainVector OneFormattedTrainVector;
17526  };
17527 
17528  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
17529 
17530  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
17531  {
17532  public:
17533  AnsiString Header;//description, mass, power, brake rate etc
17534  int NumberOfTrains;// number of repeats + 1
17535  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
17536  };
17537 
17538  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
17539  //end of formatted timetable types
17540 */
17541 
17542  // new layout using multiple rows
17543  TTFile << TableTitle.c_str() << '\n' << '\n';
17544  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
17545  {
17546  TTFile << AllTTTrains->at(x).Header.c_str();
17547  TTFile << '\n';
17548  TTFile << ','; // for the blank line above the action list
17549  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
17550  {
17551  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
17552  {
17553  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
17554  }
17555  else
17556  {
17557  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
17558  }
17559  }
17560  TTFile << '\n' << '\n';
17561 
17562  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
17563  {
17564  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
17565  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
17566  {
17567  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
17568  {
17569  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
17570  }
17571  else
17572  {
17573  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
17574  }
17575  }
17576  TTFile << '\n';
17577  }
17578  TTFile << '\n' << '\n';
17579  }
17580 
17581  TTFile.close();
17582 
17583  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17584 
17585  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
17586 
17587  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
17588 
17589  if(TTFile2 == 0)
17590  {
17591  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
17592  delete AllTTTrains;
17593  Utilities->CallLogPop(1710);
17594  return;
17595  }
17596  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
17597  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
17598  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
17599 
17600  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
17601  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
17602 
17603  // multimap of AnsiStrings with TimeString as key (to sort automatically)
17604 
17605  TTFile2 << TableTitle.c_str() << '\n' << '\n';
17606  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
17607  {
17608  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
17609  {
17610  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
17611  {
17612  bool GiveMessagesFalse = false;
17613  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
17614  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
17615  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
17616  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
17617  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
17618  {
17619  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
17620  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
17621  TimeString = TimeString.SubString(9, 5);
17622  ActionString += " " + OtherHeadCode;
17623  }
17624  if(TimeString.SubString(1, 7) == "End at ")
17625  // for Frh-sh final entry
17626  {
17627  TimeString = TimeString.SubString(8, 5);
17628  }
17629  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
17630  AnsiMultiMapEntry.first = TimeString;
17631  AnsiMultiMapEntry.second = OneLine;
17632  TAMM->insert(AnsiMultiMapEntry);
17633  }
17634  }
17635  }
17636 
17637  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
17638  {
17639  TTFile2 << (AMMIT->second).c_str();
17640  }
17641  delete AllTTTrains;
17642  delete TAMM;
17643  TTFile2.close();
17644  Utilities->CallLogPop(1580);
17645 }
17646 
17647 // ---------------------------------------------------------------------------
17648 
17649 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
17650  bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
17651 {
17652 
17653  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
17654  bool AnalysisError = false;
17655  AnsiString SequenceLog = "SequenceLog\n";
17656 
17657 /* Double crosslink (shuttle) table:
17658 
17659 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
17660  Code ShuttleLink- EntryPtr ShuttleLink-
17661  HeadCode EntryPtr
17662 
17663 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
17664 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
17665 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
17666 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
17667 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
17668 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
17669 
17670 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
17671 */
17672 
17673  try
17674  {
17675  //New section at v2.5.0 for tt conflict analysis
17676  /*
17677  typedef std::list<AnsiString> TServiceCallingLocsList;
17678  typedef std::map<AnsiString, TServiceCallingLocsList> TAllServiceCallingLocsMap;
17679 
17681  struct TLocServiceTimes
17682  {
17683  AnsiString Location;
17684  AnsiString ServiceAndRepeatNum;
17685  AnsiString AtLocTime;
17686  AnsiString ArrTime;
17687  AnsiString DepTime;
17688  AnsiString FrhMarker;
17689  };
17690  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
17691  */
17692 
17693  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
17694  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
17695 
17696 //create TrainDataVectorCopy and populate service refs with /1, /2 etc
17697  TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
17698  TTrainDataVector::iterator TDVIt, TDVCopyIt;
17699  int Suffix = 0;
17700  int IteratorNumber = 0;
17701  AnsiString AnsiSuffix = "";
17702  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
17703  {
17704  IteratorNumber++; //first value in loop is 1
17705  Suffix = 0;
17706  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
17707  {
17708  if(TDVCopyIt->HeadCode == TDVIt->HeadCode)
17709  {
17710  Suffix++; //first value is 1
17711  AnsiSuffix = AnsiString(Suffix);
17712  TDVCopyIt->ServiceReference = TDVIt->HeadCode + "/" + AnsiSuffix; //set both the HeadCode + any forward slashes and numbers, this is because sometimes
17713  TDVCopyIt->HeadCode = TDVIt->HeadCode + "/" + AnsiSuffix; //service refs are used and sometimes H/Cs, so need them to be the same,
17714  } //they are all unique at this point anyway
17715  }
17716  }
17717 //now make all linked pointers in ActionVectorEntries point to links in the vector copy (still point to original vector at this stage)
17718 //and set the linked headcodes to the correct values - all unique at this point
17719  int Increment = 0, SlashPos;
17720  TActionVectorIterator AVEIt;
17721  AnsiString LinkedHeadCode;
17722 
17723  for(TDVCopyIt = TrainDataVectorCopy.begin(); TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
17724  {
17725  for(AVEIt = TDVCopyIt->ActionVector.begin(); AVEIt != TDVCopyIt->ActionVector.end(); AVEIt++)
17726  {
17727  if(AVEIt->LinkedTrainEntryPtr != NULL)
17728  {
17729  Increment = AVEIt->LinkedTrainEntryPtr - &TrainDataVector.at(0);
17730  AVEIt->LinkedTrainEntryPtr = &TrainDataVectorCopy.at(0) + Increment;
17731  //now set AVEIt->OtherHeadCode to the linked headcodes with /1, /2 etc
17732  //but note that these ARE HeadCodes and not service refs, so need to strip off any prefixes from the linked service refs
17733  LinkedHeadCode = (*AVEIt->LinkedTrainEntryPtr).ServiceReference;
17734  //now count from back until reach a '/' character or a non-integer character, if reach non-integer first then no '/' present at end of H/c (but may be one earlier as a prefix)
17735  SlashPos = 0;
17736  for(int x = LinkedHeadCode.Length(); x > 0; x--)
17737  {
17738  if(LinkedHeadCode[x] == '/')
17739  {
17740  SlashPos = LinkedHeadCode.Length() - x + 1;
17741  break;
17742  }
17743  else if((LinkedHeadCode[x] != '0') && (LinkedHeadCode[x] != '1') && (LinkedHeadCode[x] != '2') && (LinkedHeadCode[x] != '3') &&
17744  (LinkedHeadCode[x] != '4') && (LinkedHeadCode[x] != '5') && (LinkedHeadCode[x] != '6') && (LinkedHeadCode[x] != '7') &&
17745  (LinkedHeadCode[x] != '8') && (LinkedHeadCode[x] != '8'))
17746  {
17747  break;
17748  }
17749  }
17750  //now strip off any prefix
17751  AVEIt->OtherHeadCode = LinkedHeadCode.SubString(LinkedHeadCode.Length() - 3 - SlashPos, 4 + SlashPos);
17752  }
17753  else
17754  {
17755  AVEIt->OtherHeadCode = "";
17756  }
17757  if(AVEIt->NonRepeatingShuttleLinkEntryPtr != NULL)
17758  {
17759  Increment = AVEIt->NonRepeatingShuttleLinkEntryPtr - &TrainDataVector.at(0);
17760  AVEIt->NonRepeatingShuttleLinkEntryPtr = &TrainDataVectorCopy.at(0) + Increment;
17761  //now set AVEIt->NonRepeatingShuttleLinkHeadCode to the linked headcodes with /1, /2 etc
17762  //but note that these ARE HeadCodes and not service refs, so need to strip off any prefixes from the linked service refs
17763  LinkedHeadCode = (*AVEIt->NonRepeatingShuttleLinkEntryPtr).ServiceReference;
17764  //now count from back until reach a '/' character or a non-integer character, if reach non-integer first then no '/' present at end of H/c (but may be one earlier as a prefix)
17765  SlashPos = 0;
17766  for(int x = LinkedHeadCode.Length(); x > 0; x--)
17767  {
17768  if(LinkedHeadCode[x] == '/')
17769  {
17770  SlashPos = LinkedHeadCode.Length() - x + 1;
17771  break;
17772  }
17773  else if((LinkedHeadCode[x] != '0') && (LinkedHeadCode[x] != '1') && (LinkedHeadCode[x] != '2') && (LinkedHeadCode[x] != '3') &&
17774  (LinkedHeadCode[x] != '4') && (LinkedHeadCode[x] != '5') && (LinkedHeadCode[x] != '6') && (LinkedHeadCode[x] != '7') &&
17775  (LinkedHeadCode[x] != '8') && (LinkedHeadCode[x] != '8'))
17776  {
17777  break;
17778  }
17779  }
17780  //now strip off any prefix
17781  AVEIt->NonRepeatingShuttleLinkHeadCode = LinkedHeadCode.SubString(LinkedHeadCode.Length() - 3 - SlashPos, 4 + SlashPos);
17782  }
17783  else
17784  {
17785  AVEIt->NonRepeatingShuttleLinkHeadCode = "";
17786  }
17787  }
17788  }
17789  //from here only TrainDataVectorCopy used
17790  SequenceLog += "1\n";
17791  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
17792  TServiceCallingLocsList ServiceCallingLocsList;
17793  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
17794  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
17795  {
17796  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
17797  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
17798  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
17799  ServiceCallingLocsList.clear();
17800  if(ActionVector.empty())
17801  {
17802  continue;
17803  }
17804  if(ActionVector.at(0).SignallerControl)
17805  {
17806  continue;
17807  }
17808  for(unsigned int z = 0; z < ActionVector.size(); z++)
17809  {
17810  TActionVectorEntry AVE = ActionVector.at(z);
17811  if(AVE.FormatType == StartNew)
17812  {
17813  if(AVE.LocationType == AtLocation) //located Snt
17814  {
17815  ServiceCallingLocsList.push_back(AVE.LocationName);
17816  }
17817  else //unlocated Snt (could be entering at continuation)
17818  {
17820  if(TE.ActiveTrackElementName != "")
17821  {
17822  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
17823  }
17824  else
17825  {
17826  int HLoc = TE.HLoc;
17827  int VLoc = TE.VLoc;
17828  AnsiString HString;
17829  AnsiString VString;
17830  if(HLoc < 0)
17831  {
17832  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
17833  }
17834  else
17835  {
17836  HString = AnsiString(HLoc);
17837  }
17838  if(VLoc < 0)
17839  {
17840  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
17841  }
17842  else
17843  {
17844  VString = AnsiString(VLoc);
17845  }
17846  ServiceCallingLocsList.push_back(HString + '-' + VString);
17847  }
17848  }
17849  }
17850  else if(AVE.SequenceType == StartSequence) //other start entries, all located
17851  {
17852  ServiceCallingLocsList.push_back(AVE.LocationName);
17853  }
17854  else if(AVE.FormatType == TimeLoc) //z must be > 0
17855  {
17856  if(ServiceCallingLocsList.back() != AVE.LocationName)
17857  {
17858  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
17859  }
17860  }
17861  else if(AVE.FormatType == PassTime)
17862  {
17863  ServiceCallingLocsList.push_back(AVE.LocationName);
17864  }
17865  else if(AVE.FormatType == TimeTimeLoc)
17866  {
17867  ServiceCallingLocsList.push_back(AVE.LocationName);
17868  }
17869  else if(AVE.Command == "cdt") //list if not next to start or finish
17870  {
17871  if(ActionVector.at(z-1).SequenceType == StartSequence)
17872  {
17873  continue;
17874  }
17875  else if(ActionVector.at(z+1).SequenceType == FinishSequence) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
17876  {
17877  continue;
17878  }
17879  else
17880  {
17881  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
17882  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
17883  }
17884  }
17885  else if(AVE.FormatType == ExitRailway) //Fer
17886  {
17887  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
17888  AnsiString LName = TE.ActiveTrackElementName;
17889  if(LName != "")
17890  {
17891  ServiceCallingLocsList.push_back(LName);
17892  }
17893  else
17894  {
17895  int HLoc = TE.HLoc;
17896  int VLoc = TE.VLoc;
17897  AnsiString HString;
17898  AnsiString VString;
17899  if(HLoc < 0)
17900  {
17901  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
17902  }
17903  else
17904  {
17905  HString = AnsiString(HLoc);
17906  }
17907  if(VLoc < 0)
17908  {
17909  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
17910  }
17911  else
17912  {
17913  VString = AnsiString(VLoc);
17914  }
17915  ServiceCallingLocsList.push_back(HString + '-' + VString);
17916  }
17917  }
17918  }
17919  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
17920  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
17921  }
17922  //AllServiceCallingLocsMap built
17923  SequenceLog += "2\n";
17924 /*
17925 // this sequence is to test the validity of AllServiceCallingLocsMap
17926  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
17927  std::ofstream Test(TestFile.c_str());
17928 
17929  if(TestFile == 0)
17930  {
17931  ShowMessage("TestFile failed to open - can't be created");
17932  Utilities->CallLogPop();
17933  return false;
17934  }
17935 
17936  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
17937  {
17938  Test << ASCLIt->first << '\n'; //service ref
17939  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
17940  {
17941  Test << *SCLIt << '\n';
17942  }
17943  Test << "\n\n";
17944  }
17945  Test.close();
17946  Utilities->CallLogPop();
17947  return true;
17948 */
17949  //initialise variables before calc LastTTTime & build LocServiceTimesVector
17950  if(TrainDataVectorCopy.empty())
17951  {
17952  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
17953  Utilities->CallLogPop(2209);
17954  return(false);
17955  }
17956  TLocServiceTimes TLSTEntry;
17957  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
17958  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
17959  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
17960  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
17961  LastTTTime = "";
17962  SequenceLog += "3\n";
17963  //calculate LastTTTime
17964  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
17965  {
17966  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
17967  TActionVector &ActionVector = TrainDataEntry.ActionVector;
17968  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
17969  TDateTime LastTDTime;
17970  int IncMinutes = 0;
17971  NumTrains = TrainDataEntry.NumberOfTrains;
17972  if(ActionVector.empty())
17973  {
17974  continue;
17975  }
17976  if(ActionVector.at(0).SignallerControl)
17977  {
17978  continue;
17979  }
17980  if(AVLast->FormatType == Repeat)
17981  {
17982  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
17983  AVLast--; //now points to the command before the repeat
17984  }
17985  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
17986  {
17987  AVLast--; //points to last timed entry
17988  }
17989  //here AVLast points to last entry with a time
17990  if(AVLast->ArrivalTime != TDateTime(-1))
17991  {
17992  LastTDTime = AVLast->ArrivalTime;
17993  }
17994  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
17995  {
17996  LastTDTime = AVLast->EventTime;
17997  }
17998  else
17999  {
18000  continue; //shouldn't ever reach here but if do then skip this service
18001  }
18002  if(NumTrains == 1)
18003  {
18004  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
18005  }
18006  else
18007  {
18008  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
18009  }
18010  if(LastAnsiTime > LastTTTime)
18011  {
18012  LastTTTime = LastAnsiTime;
18013  }
18014  }
18015  SequenceLog += "4\n";
18016 //build LocServiceTimesVector
18017 
18018 /*
18019  struct TLocServiceTimes
18020  {
18021  AnsiString Location;
18022  AnsiString ServiceAndRepeatNum;
18023  AnsiString AtLocTime;
18024  AnsiString ArrTime;
18025  AnsiString DepTime;
18026  AnsiString FrhMarker;
18027  };
18028  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
18029 
18030 This works as follows:
18031 ServiceAndRepeatNum is taken from the TrainDataVectorCopy as it is the same for all actionvector entries
18032 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
18033 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
18034 
18035 Every action for every train is examined and times entered as follows:-
18036 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
18037 b) an unlocated Snt: entry time becomes DepTime
18038 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
18039 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
18040 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
18041 f) TimeTimeLoc: Arrival time entered as ArrTime, a check if Arr & Dep same and if so go in as one entry, else all minutes between entered as AtLocs then DepTime
18042 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
18043 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
18044 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
18045 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
18046 */
18047  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
18048  {
18049  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
18050  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
18051  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
18052  int IncMinutes = 0;
18053  NumTrains = TrainDataEntry.NumberOfTrains;
18054  if(ActionVector.empty())
18055  {
18056  continue;
18057  }
18058  if(ActionVector.at(0).SignallerControl)
18059  {
18060  continue;
18061  }
18062  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
18063  {
18064  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
18065  }
18066  for(int y = 0; y < NumTrains; y++) //y is the repeat number
18067  {
18068  if(NumTrains == 1)
18069  {
18070  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
18071  }
18072  else if(y == 0)
18073  {
18074  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
18075  }
18076  else
18077  {
18078  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
18079  }
18080  for(unsigned int z = 0; z < ActionVector.size(); z++)
18081  {
18082  TActionVectorEntry AVE = ActionVector.at(z);
18083  TLSTEntry.AtLocTime = "";
18084  TLSTEntry.ArrTime = "";
18085  TLSTEntry.DepTime = "";
18086  TLSTEntry.Location = "";
18087  TLSTEntry.FrhMarker = "";
18088 
18089  if(AVE.FormatType == StartNew) //Snt only
18090  {
18091  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
18092  {
18093  TLSTEntry.Location = AVE.LocationName;
18094  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
18095  LocServiceTimesVector.push_back(TLSTEntry);
18096 
18097  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
18098  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
18099  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
18100  {
18101  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
18102  {
18103  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
18104  break;
18105  }
18106  if(ActionVector.at(a).SequenceType == FinishSequence) //finish catered in a later test
18107  {
18108  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
18109  break;
18110  }
18111  }
18112  if(FoundStopTime == "")
18113  {
18114  throw Exception("Failure to determine FoundStopTime for located Snt");
18115  }
18116  int WhileCount = 0;
18117  while(true)
18118  {
18119  //add minutes until reach FoundStopTime but don't add that time
18120  WhileCount++;
18121  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18122  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
18123  TLSTEntry.DepTime = "";
18124  TLSTEntry.ArrTime = "";
18125  if(IncTime >= FoundStopTime) //don't add that time
18126  {
18127  break;
18128  }
18129  LocServiceTimesVector.push_back(TLSTEntry);
18130  if(WhileCount > 2000)
18131  {
18132  throw Exception("While loop failed to break in 2000 loops for located Snt");
18133  }
18134  }
18135  }
18136  else //unlocated Snt, use the EventTime as DepTime for this vector
18137  {
18139  if(TE.ActiveTrackElementName != "")
18140  {
18141  TLSTEntry.Location = TE.ActiveTrackElementName;
18142  }
18143  else
18144  {
18145  int HLoc = TE.HLoc;
18146  int VLoc = TE.VLoc;
18147  AnsiString HString;
18148  AnsiString VString;
18149  if(HLoc < 0)
18150  {
18151  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18152  }
18153  else
18154  {
18155  HString = AnsiString(HLoc);
18156  }
18157  if(VLoc < 0)
18158  {
18159  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18160  }
18161  else
18162  {
18163  VString = AnsiString(VLoc);
18164  }
18165  TLSTEntry.Location = HString + '-' + VString;
18166  }
18167  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
18168  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
18169  LocServiceTimesVector.push_back(TLSTEntry);
18170  }
18171  }
18172 
18173  else if(AVE.SequenceType == StartSequence) //other start entries, all located
18174  {
18175  TLSTEntry.Location = AVE.LocationName;
18176  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
18177  LocServiceTimesVector.push_back(TLSTEntry);
18178  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
18179  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
18180  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
18181  {
18182  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
18183  {
18184  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
18185  break;
18186  }
18187  if(ActionVector.at(a).SequenceType == FinishSequence) //finish catered in a later test
18188  {
18189  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
18190  break;
18191  }
18192  }
18193  if(FoundStopTime == "")
18194  {
18195  throw Exception("Failure to determine FoundStopTime for SequenceType == StartSequence");
18196  }
18197  int WhileCount = 0;
18198  while(true)
18199  {
18200  //add minutes until reach FoundStopTime but don't add that time
18201  WhileCount++;
18202  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18203  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
18204  TLSTEntry.DepTime = "";
18205  TLSTEntry.ArrTime = "";
18206  if(IncTime >= FoundStopTime) //don't add that time
18207  {
18208  break;
18209  }
18210  LocServiceTimesVector.push_back(TLSTEntry);
18211  if(WhileCount > 2000)
18212  {
18213  throw Exception("While loop failed to break in 2000 loops for SequenceType == StartSequence");
18214  }
18215  }
18216  }
18217 
18218  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if arrival add in all mins to the departure or finish
18219  {
18220  TLSTEntry.Location = AVE.LocationName;
18221  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
18222  {
18223  bool SkipAddingMinutes = false;
18224  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
18225  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
18226  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
18227  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
18228  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
18229  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
18230  {
18231  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
18232  {
18233  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
18234  break;
18235  }
18236  if(ActionVector.at(a).SequenceType == FinishSequence) //finish catered for in a later test
18237  {
18238  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
18239  if((a <= (z + 2)) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr != NULL) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr != NULL)))
18240  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
18241  //at v2.10.0 changed (a == z + 1) to (a <= (z + 2)) as can have a cdt between, this allows for that
18242  {
18243  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
18244  SkipAddingMinutes = true;
18245  }
18246  break;
18247  }
18248  }
18249  if(FoundStopTime == "")
18250  {
18251  throw Exception("Failure to determine FoundStopTime for SequenceType == StartSequence");
18252  }
18253  if(!SkipAddingMinutes)
18254  {
18255  int WhileCount = 0;
18256  while(true)
18257  {
18258  //add minutes until reach FoundStopTime but don't add that time
18259  WhileCount++;
18260  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18261  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
18262  TLSTEntry.DepTime = "";
18263  TLSTEntry.ArrTime = "";
18264  if(IncTime >= FoundStopTime) //don't add that time
18265  {
18266  break;
18267  }
18268  LocServiceTimesVector.push_back(TLSTEntry);
18269  if(WhileCount > 2000)
18270  {
18271  throw Exception("While loop failed to break in 2000 loops for SequenceType == StartSequence");
18272  }
18273  }
18274  }
18275  }
18276  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
18277  {
18278  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
18279  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
18280  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
18281  {
18282  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
18283  {
18284  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
18285  LocServiceTimesVector.pop_back();
18286  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
18287  }
18288  else //just add the dep & atloc times
18289  {
18290  TLSTEntry.ArrTime = "";
18291  LocServiceTimesVector.push_back(TLSTEntry);
18292  }
18293  }
18294  else //just add the dep & atloc times
18295  {
18296  TLSTEntry.ArrTime = "";
18297  LocServiceTimesVector.push_back(TLSTEntry);
18298  }
18299  }
18300  }
18301 
18302  else if(AVE.FormatType == TimeTimeLoc)
18303  {
18304  TLSTEntry.Location = AVE.LocationName;
18305  if(AVE.ArrivalTime > TDateTime(-1)) //should be
18306  {
18307  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
18308  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
18309  }
18310  if(AVE.DepartureTime > TDateTime(-1)) //should be
18311  {
18312  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
18313  }
18314  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
18315  {
18316  LocServiceTimesVector.push_back(TLSTEntry);
18317  }
18318  else
18319  {
18320  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
18321  TLSTEntry.DepTime = "";
18322  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
18323  TLSTEntry.ArrTime = ""; //done with this now
18324  while(TLSTEntry.AtLocTime < TempDepTime)
18325  {
18326  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18327  if(TLSTEntry.AtLocTime == TempDepTime)
18328  {
18329  TLSTEntry.DepTime = TempDepTime; //restore value
18330  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
18331  }
18332  else
18333  {
18334  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
18335  }
18336  }
18337  }
18338  }
18339 
18340  else if(AVE.FormatType == PassTime) //added at v2.9.1
18341  { //adds 2 entries, 1st with PassTime as ArrTime and AtLocTime, 2nd with PassTime as AtLocTime & DepTime
18342  TLSTEntry.Location = AVE.LocationName;;
18343  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(73, AVE.EventTime, y, IncMinutes));
18344  TLSTEntry.ArrTime = TLSTEntry.AtLocTime; //DepTime already set to null
18345  LocServiceTimesVector.push_back(TLSTEntry); //1st entry
18346  TLSTEntry.ArrTime = ""; //need to reset this to null
18347  TLSTEntry.DepTime = TLSTEntry.AtLocTime;
18348  LocServiceTimesVector.push_back(TLSTEntry); //2nd entry
18349  }
18350 
18351  else if(AVE.FormatType == ExitRailway) //Fer
18352  {
18353  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
18354  //don't know which exit will be used during operation so use the first in ExitList, if several with different names then will
18355  //be wrong, but can't guess from here & most will have same name
18356  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
18357  if(LName != "")
18358  {
18359  TLSTEntry.Location = LName;
18360  }
18361  else
18362  {
18363  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
18364  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
18365  AnsiString HString;
18366  AnsiString VString;
18367  if(HLoc < 0)
18368  {
18369  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18370  }
18371  else
18372  {
18373  HString = AnsiString(HLoc);
18374  }
18375  if(VLoc < 0)
18376  {
18377  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18378  }
18379  else
18380  {
18381  VString = AnsiString(VLoc);
18382  }
18383  TLSTEntry.Location = HString + '-' + VString;
18384  }
18385  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
18386  }
18387 
18388  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
18389  {
18390  AnsiString FrhTime;
18391  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
18392  {
18393  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
18394  }
18395  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
18396  {
18397  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
18398  }
18399  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
18400  TLSTEntry.Location = AVE.LocationName;
18401  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18402  TLSTEntry.FrhMarker = "Frh";
18403  LocServiceTimesVector.push_back(TLSTEntry);
18404  TLSTEntry.FrhMarker = "";
18405  //add all times from next minute to end of timetable
18406  while(IncTime <= LastTTTime)
18407  {
18408  TLSTEntry.AtLocTime = IncTime;
18409  LocServiceTimesVector.push_back(TLSTEntry);
18410  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
18411  }
18412  }
18413 
18414  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
18415  {
18416  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
18417  {
18418  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
18419  TLSTEntry.Location = AVE.LocationName;
18420  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18421  TLSTEntry.FrhMarker = "Frh";
18422  LocServiceTimesVector.push_back(TLSTEntry);
18423  TLSTEntry.FrhMarker = "";
18424  //add all times from next minute to end of timetable
18425  while(IncTime <= LastTTTime)
18426  {
18427  TLSTEntry.AtLocTime = IncTime;
18428  LocServiceTimesVector.push_back(TLSTEntry);
18429  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
18430  }
18431  }
18432  }
18433 
18434  else if(AVE.SequenceType == FinishSequence) //other finish types - all located & all link to another service
18435  {
18436  //nothing is done here as the entry will be listed at this time under the new service reference
18437  }
18438  }
18439  }
18440  }
18441  SequenceLog += "5\n";
18442  //now sort in location order
18443  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
18444  //LocServiceTimesVector now complete & sorted in location order
18445 
18446 /*
18447 //start of debugging section
18448 //create LocServiceTimesVector output file for debugging purposes
18449  AnsiString LSTVTestFile = CurDir + "\\Formatted timetables\\LSTVTestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
18450  std::ofstream LSTVFile(LSTVTestFile.c_str());
18451  for(TLocServiceTimesVector::iterator LSTVIt = LocServiceTimesVector.begin(); LSTVIt != LocServiceTimesVector.end(); LSTVIt++)
18452  {
18453  LSTVFile << LSTVIt->Location + '\n';
18454  LSTVFile << LSTVIt->ServiceAndRepeatNum + '\n';
18455  LSTVFile << "AtLocTime = " + LSTVIt->AtLocTime + '\n';
18456  LSTVFile << "ArrTime = " + LSTVIt->ArrTime + '\n';
18457  LSTVFile << "DepTime = " + LSTVIt->DepTime + '\n';
18458  if(LSTVIt->FrhMarker == "")
18459  {
18460  LSTVFile << "Not Frh\n";
18461  }
18462  else
18463  {
18464  LSTVFile << LSTVIt->FrhMarker + '\n';
18465  }
18466  LSTVFile << '\n';
18467  }
18468  LSTVFile.close();
18469  Utilities->CallLogPop();
18470  return(true);
18471 //end of debugging section
18472 */
18473  //declare pointers for use in printouts
18474  TLocServiceTimesVector::iterator Ptr1, Ptr2;
18475 
18476  //set up the output file
18477  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
18478  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
18479 
18480  std::ofstream TTFile3(TTFileName3.c_str());
18481 
18482  if(TTFile3 == 0)
18483  {
18484  ShowMessage("Conflict Analysis file failed to open - can't be created");
18485  Utilities->CallLogPop(2210);
18486  return(false);
18487  }
18488  if(LocServiceTimesVector.empty())
18489  {
18490  ShowMessage("No timetabled services found");
18491  TTFile3.close();
18492  DeleteFile(TTFileName3);
18493  Utilities->CallLogPop(2211);
18494  return(false);
18495  }
18496  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n";
18497  TTFile3 << "See user manual or on-screen help section 5.12 for detailed information.\n\n\n";
18498  SequenceLog += "6\n";
18499 
18500 /*
18501 //print out TrainDataVectorCopy & TrainDataVector for debugging purposes, TrainDataVectorCopy first
18502 
18503 // Double crosslink (shuttle) table:
18504 //Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
18505 // Code ShuttleLink- EntryPtr ShuttleLink-
18506 // HeadCode EntryPtr
18507 
18508 //Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
18509 //Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
18510 //F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
18511 //Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
18512 //Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
18513 //Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
18514 //
18515 //Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
18516 
18517 //NOTE: from above for F-nshs & Sns-fsh in the TrainDataVectorCopy the OtherLinkedHeadCode will be correct as it is derived from LinkTrainEntryPtr which is correct
18518 //but for the original TrainDataVector the OtherLinkedHeadCode will be incorrect, hence have to use the NonRepeatingShuttleLinkHeadCode as that is the correct one
18519 //these were errors when first coded but work ok, just keep in mind when making any changes
18520 
18521 std::ofstream TDVCFile((CurDir + "\\Formatted timetables\\Conflict Analysis " + TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss") + "; " + RailwayTitle + "; " + TimetableTitle + " TrainDataVectorCopy.txt").c_str());
18522 AnsiString OHC = "", NRHC = "";
18523 AnsiString OLk = "", NRLk = "";
18524 TDVCFile << "Note that for the TrainDataVectorCopy OH and OLk should always be the same, as OH is derived from OHLk; and similarly for NR and NRLk\n\n";
18525 for(TTrainDataVector::iterator TDVCIt = TrainDataVectorCopy.begin(); TDVCIt != TrainDataVectorCopy.end(); TDVCIt++)
18526 {
18527  TDVCFile << TDVCIt->ServiceReference + '\n';
18528  TDVCFile << TDVCIt->Description + '\n';
18529  for(unsigned int x = 0; x < TDVCIt->ActionVector.size(); x++)
18530  {
18531  TActionVectorEntry AVE = TDVCIt->ActionVector.at(x);
18532  if(AVE.OtherHeadCode == "")
18533  {
18534  OHC = "OH 0";
18535  }
18536  else
18537  {
18538  OHC = "OH " + AVE.OtherHeadCode;
18539  }
18540  if(AVE.NonRepeatingShuttleLinkHeadCode == "")
18541  {
18542  NRHC = "NR 0";
18543  }
18544  else
18545  {
18546  NRHC = "NR " + AVE.NonRepeatingShuttleLinkHeadCode;
18547  }
18548  if(TDVCIt->ActionVector.at(x).LinkedTrainEntryPtr == 0)
18549  {
18550  OLk = "OLk 0";
18551  }
18552  else
18553  {
18554  OLk = "OLk " + TDVCIt->ActionVector.at(x).LinkedTrainEntryPtr->ServiceReference;
18555  }
18556  if(TDVCIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr == 0)
18557  {
18558  NRLk = "NRLk 0";
18559  }
18560  else
18561  {
18562  NRLk = "NRLk " + TDVCIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18563  }
18564 
18565  if(AVE.FormatType == TimeCmd) //cdt only
18566  {
18567  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << '\n';
18568  }
18569  if((AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle))
18570  {
18571  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18572  }
18573  else if((AVE.FormatType == FSHNewService) || (AVE.FormatType == SNSShuttle)) //these should have 2 linked services
18574  {
18575  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18576  }
18577  else if(AVE.FormatType == SNSNonRepeatFromShuttle)
18578  {
18579  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18580  }
18581  else if(AVE.FormatType == StartNew)
18582  {
18583  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt RearStartID " << Track->TrackElementAt(-1, AVE.RearStartOrRepeatMins).ElementID
18584  << " FrontStartID " << Track->TrackElementAt(-2, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18585  }
18586  else if(AVE.FormatType == SNTShuttle)
18587  {
18588  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt-sh RearStartID " << Track->TrackElementAt(-3, AVE.RearStartOrRepeatMins).ElementID
18589  << " FrontStartID " << Track->TrackElementAt(-4, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18590  }
18591  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18592  {
18593  TDVCFile << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
18594  }
18595  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
18596  {
18597  TDVCFile << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
18598  }
18599  else if(AVE.FormatType == TimeTimeLoc)
18600  {
18601  TDVCFile << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
18602  }
18603  else if(AVE.FormatType == PassTime)
18604  {
18605  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
18606  }
18607  else if(AVE.FormatType == ExitRailway)
18608  {
18609  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << " Fer" << '\n';
18610  }
18611  else if(AVE.FormatType == FinRemHere)
18612  {
18613  TDVCFile << "Frh" << '\n';
18614  }
18615  }
18616  TDVCFile << '\n';
18617 }
18618 TDVCFile.close();
18619 
18620 //print out original TrainDataVector for comparison
18621 std::ofstream TDVFile((CurDir + "\\Formatted timetables\\Conflict Analysis " + TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss") + "; " + RailwayTitle + "; " + TimetableTitle + " TrainDataVector.txt").c_str());
18622 TDVFile << "Note that in the TrainDataVector, non-repeating shuttle link services F-nshs and Sns-fsh use the non-repeating headcode (NR) values for the corresponding "
18623  "shuttle headcodes when it should be the other headcode (OH), and the other headcode is unused. The link values are the right way round. Also OH & NR "
18624  "values ARE headcodes and not service references, but OLk and NRLk values are service references.\n\n";
18625 //F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
18626 //Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
18627 for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
18628 {
18629  TDVFile << TDVIt->ServiceReference + '\n';
18630  TDVFile << TDVIt->Description + '\n';
18631  for(unsigned int x = 0; x < TDVIt->ActionVector.size(); x++)
18632  {
18633  TActionVectorEntry AVE = TDVIt->ActionVector.at(x);
18634  if(AVE.OtherHeadCode == "")
18635  {
18636  OHC = "OH 0";
18637  }
18638  else
18639  {
18640  OHC = "OH " + AVE.OtherHeadCode;
18641  }
18642  if(AVE.NonRepeatingShuttleLinkHeadCode == "")
18643  {
18644  NRHC = "NR 0";
18645  }
18646  else
18647  {
18648  NRHC = "NR " + AVE.NonRepeatingShuttleLinkHeadCode;
18649  }
18650  if(TDVIt->ActionVector.at(x).LinkedTrainEntryPtr == 0)
18651  {
18652  OLk = "OLk 0";
18653  }
18654  else
18655  {
18656  OLk = "OLk " + TDVIt->ActionVector.at(x).LinkedTrainEntryPtr->ServiceReference;
18657  }
18658  if(TDVIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr == 0)
18659  {
18660  NRLk = "NRLk 0";
18661  }
18662  else
18663  {
18664  NRLk = "NRLk " + TDVIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18665  }
18666 
18667  if(AVE.FormatType == TimeCmd) //cdt only
18668  {
18669  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << '\n';
18670  }
18671  if((AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle))
18672  {
18673  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18674  }
18675  else if((AVE.FormatType == FSHNewService) || (AVE.FormatType == SNSShuttle)) //these should have 2 linked services
18676  {
18677  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18678  }
18679  else if(AVE.FormatType == SNSNonRepeatFromShuttle)
18680  {
18681  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18682  }
18683  else if(AVE.FormatType == StartNew)
18684  {
18685  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt RearStartID " << Track->TrackElementAt(-5, AVE.RearStartOrRepeatMins).ElementID
18686  << " FrontStartID " << Track->TrackElementAt(-6, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18687  }
18688  else if(AVE.FormatType == SNTShuttle)
18689  {
18690  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt-sh RearStartID " << Track->TrackElementAt(-7, AVE.RearStartOrRepeatMins).ElementID
18691  << " FrontStartID " << Track->TrackElementAt(-8, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18692  }
18693  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18694  {
18695  TDVFile << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
18696  }
18697  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
18698  {
18699  TDVFile << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
18700  }
18701  else if(AVE.FormatType == TimeTimeLoc)
18702  {
18703  TDVFile << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
18704  }
18705  else if(AVE.FormatType == PassTime)
18706  {
18707  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
18708  }
18709  else if(AVE.FormatType == ExitRailway)
18710  {
18711  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << " Fer" << '\n';
18712  }
18713  else if(AVE.FormatType == FinRemHere)
18714  {
18715  TDVFile << "Frh" << '\n';
18716  }
18717  }
18718  TDVFile << '\n';
18719 }
18720 TDVFile.close();
18721 //end of debugging
18722 */
18723  //arrivals
18724  if(ArrChecked)
18725  {
18726  //sort in ArrTime order for each location
18727  Ptr1 = LocServiceTimesVector.begin();
18728  Ptr2 = Ptr1 + 1;
18729  while(Ptr2 != LocServiceTimesVector.end())
18730  {
18731  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
18732  {
18733  Ptr2++;
18734  if(Ptr2 == LocServiceTimesVector.end())
18735  {
18736  break;
18737  }
18738  }
18739  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
18740  Ptr1 = Ptr2; //first entry with next name
18741  if(Ptr2 != LocServiceTimesVector.end())
18742  {
18743  Ptr2++;
18744  }
18745  }
18746 
18747  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
18748 
18749  TTFile3 << "Arrival & pass analysis: an asterisk means that the number of same approach code arrivals and passes is equal to or greater than the number of platforms.\n";
18750  TTFile3 << "If the total number of arrivals and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
18751  MinuteString = " minutes";
18752  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
18753  if(ArrRange == 1)
18754  {
18755  MinuteString = " minute";
18756  }
18757  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
18758  TTFile3 << ",Platforms,Trains\n\n";
18759 
18760  Ptr1 = LocServiceTimesVector.begin();
18761  Ptr2 = Ptr1 + 1;
18762  while(Ptr2 != LocServiceTimesVector.end())
18763  {
18764  PreviousService = "";
18765  NumTrainsAtLoc = 0;
18766  ServiceAndRepeatNumTotal = "";
18767  NumPlats = 0;
18768  NumPlatsAtThisLocCalculated = false;
18769  BasicTime = "";
18770  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
18771  {
18772  PreviousService = "";
18773  NumTrainsAtLoc = 0;
18774  ServiceAndRepeatNumTotal = "";
18775  NumPlats = 0;
18776  NumPlatsAtThisLocCalculated = false;
18777  BasicTime = "";
18778  Ptr1++;
18779  Ptr2++;
18780  if(Ptr2 == LocServiceTimesVector.end())
18781  {
18782  break;
18783  }
18784  }
18785  if(Ptr2 == LocServiceTimesVector.end())
18786  {
18787  break;
18788  }
18789  while(Ptr2->Location == Ptr1->Location)
18790  {
18791  PreviousService = "";
18792  NumTrainsAtLoc = 0;
18793  ServiceAndRepeatNumTotal = "";
18794  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
18795  if((Ptr1->Location == "") && (Ptr2->Location == ""))
18796  {
18797  break;
18798  }
18799  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
18800  {
18801  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
18802  Ptr1++;
18803  Ptr2++;
18804  if(Ptr2 == LocServiceTimesVector.end())
18805  {
18806  break;
18807  }
18808  if(Ptr2->Location != Ptr1->Location)
18809  {
18810  break;
18811  }
18812  }
18813  if(Ptr2 == LocServiceTimesVector.end())
18814  {
18815  break;
18816  }
18817  if(Ptr2->Location != Ptr1->Location)
18818  {
18819  break;
18820  }
18821  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
18822  {
18823  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
18824  {
18825  break;
18826  }
18827  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
18828  {
18829  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
18830  NumPlatsAtThisLocCalculated = true;
18831  }
18832  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
18833  {
18834  if(ServiceAndRepeatNumTotal == "")
18835  {
18836  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
18837  NumTrainsAtLoc = 1;
18838  }
18839  else
18840  {
18841  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
18842  }
18843  }
18844  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
18845  if(ServiceAndRepeatNumTotal == "")
18846  {
18847  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
18848  NumTrainsAtLoc = 1;
18849  }
18850  else
18851  {
18852  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
18853  }
18854  Ptr1 = Ptr2;
18855  Ptr2++;
18856  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
18857  {
18858  int MaxNumberOfSameDirections = 0;
18859  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
18860  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
18861  {
18862 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
18863  TTFile3.close();
18864  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
18865 // Utilities->CallLogPop(2224);
18866 // return false;
18867  }
18868  AnsiString Asterisk = "";
18869  if(MaxNumberOfSameDirections >= NumPlats)
18870  {
18871  Asterisk = "* ";
18872  }
18873  //print out a single line for number of trains at loc with all service refs
18874  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
18875  ArrivalsPrinted = true;
18876  ServiceAndRepeatNumTotal = "";
18877  }
18878  if(Ptr2 == LocServiceTimesVector.end())
18879  {
18880  break;
18881  }
18882  if(Ptr2->Location != Ptr1->Location)
18883  {
18884  break;
18885  }
18886  }
18887  if(Ptr2 == LocServiceTimesVector.end())
18888  {
18889  break;
18890  }
18891  }
18892  }
18893  if(!ArrivalsPrinted)
18894  {
18895  TTFile3 << "Nothing to report for arrivals";
18896  }
18897  TTFile3 << "\n\n";
18898  }
18899  //end of routine for arrivals
18900  SequenceLog += "7\n";
18901  //departures
18902  if(DepChecked)
18903  {
18904  //sort in DepTime order for each location
18905  Ptr1 = LocServiceTimesVector.begin();
18906  Ptr2 = Ptr1 + 1;
18907  while(Ptr2 != LocServiceTimesVector.end())
18908  {
18909  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
18910  {
18911  Ptr2++;
18912  if(Ptr2 == LocServiceTimesVector.end())
18913  {
18914  break;
18915  }
18916  }
18917  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
18918  Ptr1 = Ptr2; //first entry with next name
18919  if(Ptr2 != LocServiceTimesVector.end())
18920  {
18921  Ptr2++;
18922  }
18923  }
18924 
18925  //routine for departures - number of trains departing within the specified range with services listed at the end
18926  TTFile3 << "Departure & pass analysis: an asterisk means that the number of same exit code departures and passes is equal to or greater than the number of platforms.\n";
18927  TTFile3 << "If the total number of departures and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
18928  MinuteString = " minutes";
18929  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
18930  if(DepRange == 1)
18931  {
18932  MinuteString = " minute";
18933  }
18934  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
18935  TTFile3 << ",Platforms,Trains\n\n";
18936 
18937  Ptr1 = LocServiceTimesVector.begin();
18938  Ptr2 = Ptr1 + 1;
18939  while(Ptr2 != LocServiceTimesVector.end())
18940  {
18941  PreviousService = "";
18942  NumTrainsAtLoc = 0;
18943  ServiceAndRepeatNumTotal = "";
18944  NumPlats = 0;
18945  NumPlatsAtThisLocCalculated = false;
18946  BasicTime = "";
18947  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
18948  {
18949  PreviousService = "";
18950  NumTrainsAtLoc = 0;
18951  ServiceAndRepeatNumTotal = "";
18952  NumPlats = 0;
18953  NumPlatsAtThisLocCalculated = false;
18954  BasicTime = "";
18955  Ptr1++;
18956  Ptr2++;
18957  if(Ptr2 == LocServiceTimesVector.end())
18958  {
18959  break;
18960  }
18961  }
18962  if(Ptr2 == LocServiceTimesVector.end())
18963  {
18964  break;
18965  }
18966  while(Ptr2->Location == Ptr1->Location)
18967  {
18968  PreviousService = "";
18969  NumTrainsAtLoc = 0;
18970  ServiceAndRepeatNumTotal = "";
18971  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
18972  if((Ptr1->Location == "") && (Ptr2->Location == ""))
18973  {
18974  break;
18975  }
18976  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
18977  {
18978  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
18979  Ptr1++;
18980  Ptr2++;
18981  if(Ptr2 == LocServiceTimesVector.end())
18982  {
18983  break;
18984  }
18985  if(Ptr2->Location != Ptr1->Location)
18986  {
18987  break;
18988  }
18989  }
18990  if(Ptr2 == LocServiceTimesVector.end())
18991  {
18992  break;
18993  }
18994  if(Ptr2->Location != Ptr1->Location)
18995  {
18996  break;
18997  }
18998  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
18999  {
19000  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
19001  {
19002  break;
19003  }
19004  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
19005  {
19006  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
19007  NumPlatsAtThisLocCalculated = true;
19008  }
19009  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
19010  {
19011  if(ServiceAndRepeatNumTotal == "")
19012  {
19013  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
19014  NumTrainsAtLoc = 1;
19015  }
19016  else
19017  {
19018  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
19019  }
19020  }
19021  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
19022  if(ServiceAndRepeatNumTotal == "")
19023  {
19024  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
19025  NumTrainsAtLoc = 1;
19026  }
19027  else
19028  {
19029  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
19030  }
19031  Ptr1 = Ptr2;
19032  Ptr2++;
19033  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
19034  {
19035  int MaxNumberOfSameDirections = 0;
19036  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
19037  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
19038  {
19039 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
19040  TTFile3.close();
19041  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
19042 // Utilities->CallLogPop(2225);
19043 // return false;
19044  }
19045  AnsiString Asterisk = "";
19046  if(MaxNumberOfSameDirections >= NumPlats)
19047  {
19048  Asterisk = "* ";
19049  }
19050  //print out a single line for number of trains at loc with all service refs
19051  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
19052  DeparturesPrinted = true;
19053  ServiceAndRepeatNumTotal = "";
19054  }
19055  if(Ptr2 == LocServiceTimesVector.end())
19056  {
19057  break;
19058  }
19059  if(Ptr2->Location != Ptr1->Location)
19060  {
19061  break;
19062  }
19063  }
19064  if(Ptr2 == LocServiceTimesVector.end())
19065  {
19066  break;
19067  }
19068  }
19069  }
19070  if(!DeparturesPrinted)
19071  {
19072  TTFile3 << "Nothing to report for departures";
19073  }
19074  TTFile3 << "\n\n";
19075  }
19076  //end of routine for departures
19077  SequenceLog += "8\n";
19078 
19079  //list trains at locations at same time
19080 
19081  if(AtLocChecked)
19082  {
19083  //sort in AtLocTime order for each location
19084  Ptr1 = LocServiceTimesVector.begin();
19085  Ptr2 = Ptr1 + 1;
19086  while(Ptr2 != LocServiceTimesVector.end())
19087  {
19088  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
19089  {
19090  Ptr2++;
19091  if(Ptr2 == LocServiceTimesVector.end())
19092  {
19093  break;
19094  }
19095  }
19096  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
19097  Ptr1 = Ptr2; //first entry with next name
19098  if(Ptr2 != LocServiceTimesVector.end())
19099  {
19100  Ptr2++;
19101  }
19102  }
19103 
19104  //print out simultaneous AtLocs (don't need range of times for AtLocs)
19105  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
19106  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
19107  TTFile3 << ",Platforms,Trains,\n\n";
19108  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
19109  Ptr1 = LocServiceTimesVector.begin();
19110  Ptr2 = Ptr1 + 1;
19111  while(Ptr2 != LocServiceTimesVector.end())
19112  {
19113  PreviousService = "";
19114  ServiceAndRepeatNumTotal = "";
19115  NumTrainsAtLoc = 0;
19116  NumPlats = 0;
19117  NumPlatsAtThisLocCalculated = false;
19118  FrhCount = 0;
19119 
19120  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
19121  {
19122  PreviousService = "";
19123  ServiceAndRepeatNumTotal = "";
19124  NumTrainsAtLoc = 0;
19125  NumPlats = 0;
19126  NumPlatsAtThisLocCalculated = false;
19127  FrhCount = 0;
19128  Ptr1++;
19129  Ptr2++;
19130  if(Ptr2 == LocServiceTimesVector.end())
19131  {
19132  break;
19133  }
19134  }
19135  if(Ptr2 == LocServiceTimesVector.end())
19136  {
19137  break;
19138  }
19139  while(Ptr2->Location == Ptr1->Location)
19140  {
19141  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
19142  {
19143  FrhCount++;
19144  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
19145  }
19146  PreviousService = "";
19147  NumTrainsAtLoc = 0;
19148  ServiceAndRepeatNumTotal = "";
19149  if((Ptr1->Location == "") && (Ptr2->Location == ""))
19150  {
19151  break;
19152  }
19153  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
19154  {
19155  Ptr1++;
19156  if(Ptr1->FrhMarker == "Frh")
19157  {
19158  FrhCount++;
19159  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
19160  }
19161  Ptr2++;
19162  if(Ptr2 == LocServiceTimesVector.end())
19163  {
19164  break;
19165  }
19166  if(Ptr2->Location != Ptr1->Location)
19167  {
19168  break;
19169  }
19170  }
19171  if(Ptr2 == LocServiceTimesVector.end())
19172  {
19173  break;
19174  }
19175  if(Ptr2->Location != Ptr1->Location)
19176  {
19177  break;
19178  }
19179  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
19180  {
19181  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
19182  {
19183  break;
19184  }
19185  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
19186  {
19187  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
19188  NumPlatsAtThisLocCalculated = true;
19189  }
19190  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
19191  {
19192  if(ServiceAndRepeatNumTotal == "")
19193  {
19194  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
19195  NumTrainsAtLoc = 1;
19196  }
19197  else
19198  {
19199  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
19200  }
19201  }
19202  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
19203  if(ServiceAndRepeatNumTotal == "")
19204  {
19205  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
19206  NumTrainsAtLoc = 1;
19207  }
19208  else
19209  {
19210  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
19211  }
19212  Ptr1 = Ptr2;
19213  if(Ptr1->FrhMarker == "Frh")
19214  {
19215  FrhCount++;
19216  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
19217  }
19218  Ptr2++;
19219  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
19220  {
19221 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
19222 //new text //don't print out if all remainers or if only 1 train at loc
19223  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
19224 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
19225 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
19226  {
19227  AnsiString Asterisk = "";
19228  if(NumTrainsAtLoc > NumPlats)
19229  {
19230  Asterisk = "* ";
19231  }
19232  //print out a single line for number of trains at loc with all service refs
19233  if(FrhCount == 0)
19234  {
19235  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
19236  }
19237  else if(FrhCount == 1)
19238  {
19239  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
19240  }
19241  else
19242  {
19243  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
19244  }
19245  LastFrhCount = FrhCount;
19246  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
19247  AtLocsPrinted = true;
19248  ServiceAndRepeatNumTotal = "";
19249  }
19250  }
19251  if(Ptr2 == LocServiceTimesVector.end())
19252  {
19253  break;
19254  }
19255  if(Ptr2->Location != Ptr1->Location)
19256  {
19257  break;
19258  }
19259  }
19260  if(Ptr2 == LocServiceTimesVector.end())
19261  {
19262  break;
19263  }
19264  }
19265  }
19266  if(!AtLocsPrinted)
19267  {
19268  TTFile3 << "Nothing to report for trains at locations";
19269  }
19270  TTFile3 << "\n\n";
19271  //end of simultaneous AtLocs
19272  }
19273  SequenceLog += "9\n";
19274 
19275 /*
19276 //start of debugging section
19277  //print out the full vector here for testing purposes
19278  TTFile3 << "Full LocServiceTimesVector\n\n";
19279  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
19280 
19281  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
19282  {
19283  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
19284  }
19285 
19286  TTFile3 << "\n\n\n";
19287 //end of debugging
19288 */
19289 
19290 /*cdt analysis - added at v2.10.0
19291 2 pass system: 1st extract as a single service all Snt (or Snt-sh) starts, with Fns/Sns links combined (and F-nshs/Sns-sh) (though add a new
19292 changeover code [chr XXXX - 'change ref + new reference] until come to Fjo, Frh, Frh-sh, Fer (ignore exit loc as can't stop there), ignore jbos &
19293 repeats, but with fsp & rsp store all the foregoing service entries along with the split reference & add that to the relevant Sfs entry as a new
19294 service. For shuttles with feeder start with feeder & progress into shuttle, ending when finish & remain here or progressing into the finishing
19295 service.
19296 
19297 Use The TrainDataVectorCopy as that has all unique service refs.
19298 
19299 2nd run the cdt checker similar to that in SecondPassActions, but where a same name found either side of a changeover code quote both refs. Add a
19300 similar unexpected cdt check where if have different locs either side of a cdt then may be inappropriate.
19301 
19302 First create a new TrainDataVector from earlier copy as above with single services
19303 */
19304  if(DirChecked)
19305  {
19306  //direction analysis added at v2.10.0
19307  TTrainDataEntry SingleServiceEntry, PartServiceEntry, NewPartServiceEntry, TempEntry;
19308  TTrainDataVector SingleServiceVector, PartServiceVector;
19309 
19310  //find new train services (Snt or Snt-sh) & remember that entries can be in any order
19311  //NB: ALWAYS use OtherHeadCode (which is now a service reference) for any follow-on service
19312  TTFile3 << "Train direction analysis - consisting of train facing directions on creation and possible missing or questionable changes of direction:\n\n";
19313  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
19314  {
19316  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
19317  {
19318  TDE.ActionVector.erase(TDE.ActionVector.end() - 1); //strip repeat entry if present
19319  }
19320  const TActionVector &AV = TDE.ActionVector;
19321  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
19322  {
19323  SingleServiceEntry = TDE;
19324  TActionVector &SSAV = SingleServiceEntry.ActionVector;
19325  for(unsigned int y = 0; y < SSAV.size(); y++)
19326  {
19327  if((SSAV.at(y).Command == "Fjo") || (SSAV.at(y).Command == "Frh") || (SSAV.at(y).Command == "Fer") || (SSAV.at(y).Command == "Frh-sh"))
19328  {
19329  SingleServiceVector.push_back(SingleServiceEntry); //push the complete entry
19330  break; //finished with this one
19331  }
19332  else if((SSAV.at(y).Command == "fsp") || (SSAV.at(y).Command == "rsp"))
19333  {
19334  PartServiceEntry = TDE; //start with complete entry
19335  PartServiceEntry.ActionVector.clear(); //clear AV
19336  for(unsigned int z = 0; z <= y; z++)
19337  {
19338  PartServiceEntry.ActionVector.push_back(TDE.ActionVector.at(z)); //add back all AVs up to & inc fsp/rsp
19339  if(z == y)
19340  {
19341  PartServiceEntry.ActionVector.at(z).Command = "chr-sp"; //change split command to chr
19342  PartServiceEntry.ActionVector.at(z).OtherHeadCode = PartServiceEntry.ActionVector.at(z).LinkedTrainEntryPtr->ServiceReference;
19343  }
19344  }
19345  PartServiceVector.push_back(PartServiceEntry);
19346  if(SSAV.at(y).Command == "fsp")
19347  {
19348  SSAV.at(y).Command = "Front split - original service continues below";
19349  SSAV.at(y).OtherHeadCode = "";
19350  }
19351  if(SSAV.at(y).Command == "rsp")
19352  {
19353  SSAV.at(y).Command = "Rear split - original service continues below";
19354  SSAV.at(y).OtherHeadCode = "";
19355  }
19356  //don't break & continue here because the original train carries on
19357  }
19358  else if(SSAV.at(y).Command == "Fns")
19359  {
19360  SSAV.at(y).Command = "chr-Fns";
19361  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
19362  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
19363  break; //from y loop
19364  }
19365  else if(SSAV.at(y).Command == "Fns-sh")
19366  {
19367  SSAV.at(y).Command = "chr-Fns-sh";
19368  SSAV.at(y).OtherHeadCode = SSAV.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
19369  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
19370  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
19371  break; //from y loop
19372  }
19373  else if(SSAV.at(y).Command == "F-nshs")
19374  {
19375  SSAV.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
19376  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
19377  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
19378  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
19379  break; //from y loop
19380  }
19381  }
19382  }
19383  }
19384  SequenceLog += "10\n";
19385  //now have all complete entries in SingleServiceVector and all part services in PartServiceVector but without any follow-ons
19386 
19387  //Now add Sns & Sns-sh services to PartServiceVector entries
19388  AnsiString NextRef;
19389  while(!PartServiceVector.empty())
19390  {
19391  PartServiceEntry = PartServiceVector.at(0); //deal with front entry and add new entries at the back
19392  for(unsigned int y = 0; y < PartServiceEntry.ActionVector.size(); y++)
19393  {
19394  if(PartServiceEntry.ActionVector.at(y).Command.SubString(1,3) == "chr")
19395  {
19396  NextRef = PartServiceEntry.ActionVector.at(y).OtherHeadCode;
19397  }
19398  }
19399  //find it in TrainDataVectorCopy
19400  bool FinishType = true, FoundFlag = false;
19401  while(FinishType)
19402  {
19403  TempEntry = GetServiceFromVector(0, NextRef, TrainDataVectorCopy, FinishType, FoundFlag); //FinishType is a bool where false = Final (Fjo, Frh, Fer, or
19404  //Frh-sh); true = MoreToCome (Fns, Fns-sh, F-nshs)
19405  if(FoundFlag)
19406  {
19407  for(unsigned int y = 1; y < TempEntry.ActionVector.size(); y++) //starts at 1 as that is the entry after the start entry
19408  {
19409  if((TempEntry.ActionVector.at(y).Command == "") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
19410  {
19411  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19412  }
19413  else if((TempEntry.ActionVector.at(y).Command[1] != 'F') && (TempEntry.ActionVector.at(y).Command != "fsp") && (TempEntry.ActionVector.at(y).Command != "rsp") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
19414  {
19415  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19416  }
19417  else
19418  {
19419  if((TempEntry.ActionVector.at(y).Command == "Fjo") || (TempEntry.ActionVector.at(y).Command == "Frh") || (TempEntry.ActionVector.at(y).Command == "Fer") || (TempEntry.ActionVector.at(y).Command == "Frh-sh"))
19420  {
19421  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19422  SingleServiceVector.push_back(PartServiceVector.at(0)); //push the complete entry
19423  PartServiceVector.erase(PartServiceVector.begin());
19424  break; //from y loop
19425  }
19426  else if((TempEntry.ActionVector.at(y).Command == "fsp") || (TempEntry.ActionVector.at(y).Command == "rsp"))
19427  {
19428  NewPartServiceEntry = PartServiceVector.at(0); //covers everything up to but excluding the split
19429  NewPartServiceEntry.ActionVector.push_back(TempEntry.ActionVector.at(y)); //now includes the split
19430  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).Command = "chr-sp"; //change split command to chr
19431  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).OtherHeadCode = NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).LinkedTrainEntryPtr->ServiceReference;
19432  PartServiceVector.push_back(NewPartServiceEntry); //new entry for the split service
19433  if(TempEntry.ActionVector.at(y).Command == "fsp")
19434  {
19435  TempEntry.ActionVector.at(y).Command = "Front split - original service continues below";
19436  TempEntry.ActionVector.at(y).OtherHeadCode = "";
19437  }
19438  if(TempEntry.ActionVector.at(y).Command == "rsp")
19439  {
19440  TempEntry.ActionVector.at(y).Command = "Rear split - original service continues below";
19441  TempEntry.ActionVector.at(y).OtherHeadCode = "";
19442  }
19443  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19444  }
19445  else if(TempEntry.ActionVector.at(y).Command == "Fns")
19446  {
19447  TempEntry.ActionVector.at(y).Command = "chr-Fns";
19448  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
19449  TempEntry.ActionVector.at(y).OtherHeadCode = NextRef;
19450  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
19451  break; //from y loop
19452  }
19453  else if(TempEntry.ActionVector.at(y).Command == "Fns-sh")
19454  {
19455  TempEntry.ActionVector.at(y).Command = "chr-Fns-sh";
19456  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
19457  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
19458  NextRef = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
19459  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
19460  break; //from y loop
19461  }
19462  else if(TempEntry.ActionVector.at(y).Command == "F-nshs")
19463  {
19464  TempEntry.ActionVector.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
19465  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
19466  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
19467  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
19468  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
19469  break; //from y loop
19470  }
19471  }
19472  }
19473  }
19474  else
19475  {
19476  SequenceLog += + "11\n";
19477  throw Exception("Unable to find service reference " + NextRef + " Last ref checked = " + TempEntry.ServiceReference);
19478  }
19479  }
19480  }
19481  if(!PartServiceVector.empty())
19482  {
19483  SequenceLog += "12\n";
19484  throw Exception("PartServiceVector should be empty here - size = " + AnsiString(PartServiceVector.size()));
19485  }
19486  SequenceLog += "13\n";
19487  /*
19488  form:-
19489  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
19490  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
19491  then multiple entries, separated by commas, of the form:-
19492 
19493  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
19494  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
19495  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
19496 
19497  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
19498  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
19499  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
19500 
19501  HH:MM;Command (cdt) }TimeCmd }
19502  HH:MM;Command;new description (dsc) }TimeCmdDescription }
19503  HH:MM;Location (arr & dep) }TimeLoc }
19504  HH:MM;HH:MM;Location }TimeTimeLoc }
19505  HH:MM;pas;Location }PassTime }
19506  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
19507  HH:MM;Fer;set of allowable IDs }ExitRailway }
19508  Command (Frh only) }FinRemHere }
19509 
19510  R;mm;dd;nn. Repeat Repeat entry
19511 
19512  Formats:
19513 
19514  Command only: Frh
19515  Time;Command: cdt
19516  Time;Command;new description: dsc
19517  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
19518  Time;Command;2 Element IDs: Snt
19519  Time;Comand;n Element IDs: Fer
19520  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
19521  Time;Command;2 Element IDs;Headcode Snt-sh
19522  Time;Command;Location pas
19523  Time;Location Arr Dep
19524  Time;Time;Location Arr & dep together
19525  */
19526 
19527 /*
19528 Perform the starting direction check (Snt & Snt-sh entries). Starts with the train's front element,
19529 checking forwards until it comes to a continuation (no report), a location name that is not null and
19530 different to the train's front element name (whether null or not) (no report), a leading point
19531 (no report) or buffers (report).
19532 */
19533  bool BufferFacingUnReportedFlag = true;
19534  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
19535  {
19536  TTrackElement ThisElement, NextElement;
19537  TTrainDataEntry TDE = SingleServiceVector.at(x);
19538  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
19539  {
19540  SequenceLog += "13a\n";
19541  throw Exception("Repeat entry present in SingleServiceVector at position " + AnsiString(x));
19542  }
19543  const TActionVector &AV = TDE.ActionVector;
19544  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
19545  {
19546  bool BufferFlag = false;
19547  int FrontTVPos = AV.at(0).FrontStartOrRepeatDigits;
19548  int RearTVPos = AV.at(0).RearStartOrRepeatMins;
19549  AnsiString FrontLocName = AV.at(0).LocationName;
19550  int NextEntryPos, NextExitPos;
19551  ThisElement = Track->TrackElementAt(1395, FrontTVPos);
19552  int ThisExitPos;
19553  if(ThisElement.Conn[0] == RearTVPos)
19554  {
19555  ThisExitPos = 1;
19556  }
19557  else if(ThisElement.Conn[1] == RearTVPos)
19558  {
19559  ThisExitPos = 0;
19560  }
19561  else if(ThisElement.Conn[2] == RearTVPos)
19562  {
19563  ThisExitPos = 3;
19564  }
19565  else if(ThisElement.Conn[3] == RearTVPos)
19566  {
19567  ThisExitPos = 2;
19568  }
19569  if((ThisElement.TrackType == Buffers) && (ThisExitPos == 0))//pos 0 is the buffer
19570  {
19571  BufferFlag = true;
19572  }
19573  else //continue tracking forwards
19574  {
19575  while(true)
19576  {
19577  if(ThisElement.Conn[ThisExitPos] == -1)
19578  {
19579  SequenceLog = "13b\n";
19580  throw Exception("ThisElement connects to -1 for " + TDE.ServiceReference);
19581  }
19582  NextElement = Track->TrackElementAt(1396, ThisElement.Conn[ThisExitPos]);
19583  NextEntryPos = ThisElement.ConnLinkPos[ThisExitPos];
19584  if((NextElement.TrackType == Points) && ((NextEntryPos == 0) || (NextEntryPos == 2))) //leading points
19585  {
19586  BufferFlag = false; //should already be false
19587  break;
19588  }
19589  else if(NextElement.TrackType == Continuation)
19590  {
19591  BufferFlag = false;
19592  break;
19593  }
19594  else if((NextElement.ActiveTrackElementName != "") && (NextElement.ActiveTrackElementName != FrontLocName))
19595  {
19596  BufferFlag = false;
19597  break;
19598  }
19599  else if(NextElement.TrackType == Buffers)
19600  {
19601  BufferFlag = true;
19602  break;
19603  }
19604  else if((NextElement.TrackType == Points) && ((NextEntryPos == 1) || (NextEntryPos == 3))) //trailing points
19605  {
19606  ThisElement = NextElement;
19607  ThisExitPos = 0;
19608  continue;
19609  }
19610  else
19611  {
19612  if(NextEntryPos == 0)
19613  {
19614  NextExitPos = 1;
19615  }
19616  else if(NextEntryPos == 1)
19617  {
19618  NextExitPos = 0;
19619  }
19620  else if(NextEntryPos == 2)
19621  {
19622  NextExitPos = 3;
19623  }
19624  else if(NextEntryPos == 3)
19625  {
19626  NextExitPos = 2;
19627  }
19628  }
19629  ThisElement = NextElement;
19630  ThisExitPos = NextExitPos;
19631  }
19632  }
19633  if(BufferFlag)
19634  {
19635  if(BufferFacingUnReportedFlag)
19636  {
19637  TTFile3 << "Train facing direction on creation analysis:-\n\n";
19638  BufferFacingUnReportedFlag = false;
19639  }
19640  TTFile3 << "Service " + TDE.ServiceReference + " facing buffers on creation\n";
19641  }
19642  }
19643  }
19644  if(BufferFacingUnReportedFlag)
19645  {
19646  TTFile3 << "Nothing to report for train facing directions\n\n";
19647  }
19648  else
19649  {
19650  TTFile3 << '\n';
19651  }
19652  SequenceLog += "13c\n";
19653 
19654  //Perform the missing cdt check. Check every entry similar to the check in SecondPassActions and if find any print out the full sequence and service entries
19655  AnsiString LocationNameToBeChecked = "";
19656  bool MissingcdtUnreportedFlag = true;
19657  TNumList MarkerList;
19658  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
19659  {
19660  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
19661  unsigned int y = 0;
19662  int FirstInstance = 9999, SecondInstance = 9999; //9999 ensures won't be marked if not changed
19663  bool FullBreak = false;
19664  MarkerList.clear();
19665  // first discard unlocated Snt entries as they don't have location name set
19666  while((y < TDEntry.ActionVector.size()) && !FullBreak)
19667  // need to check each location name separately in turn, skipped for SignallerControl entries
19668  {
19669  if(/*(TDEntry.ActionVector.at(y).Command == "Fer") || */(TDEntry.ActionVector.at(y).FormatType == Repeat) ||
19670  (TDEntry.ActionVector.at(y).Command == "Fjo") || (TDEntry.ActionVector.at(y).Command == "Frh") ||
19671  (TDEntry.ActionVector.at(y).Command == "Frh-sh"))
19672  {
19673  break; // out of the 'while' loop since have reached the end
19674  }
19675  LocationNameToBeChecked = ""; //this section where continuation name assigned for unlocated Snts added at v2.18.0
19676  if((TDEntry.ActionVector.at(y).Command == "Snt") && (TDEntry.ActionVector.at(y).LocationType == EnRoute)) //unlocated Snt - check if the continuation has a name
19677  {
19678  int EntryPos = TDEntry.ActionVector.at(0).RearStartOrRepeatMins; //this is a track vector position
19679  LocationNameToBeChecked = Track->TrackElementAt(1678, EntryPos).ActiveTrackElementName;
19680  }
19681  if(LocationNameToBeChecked == "")
19682  {
19683  if(y == 0) //unlocated and un-named Snt, so skip this value of y
19684  {
19685  y++;
19686  continue;
19687  }
19688  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName; //the only un-named values for ActionVectorEntry::LocationName
19689  } //are for unlocated Snts and Fers
19690  FirstInstance = y;
19691  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
19692  {
19693  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
19694  if(/*(AVEntry.Command == "Fer") || */(AVEntry.FormatType == Repeat) ||
19695  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
19696  (AVEntry.Command == "Frh-sh"))
19697  {
19698  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
19699  }
19700  if(AVEntry.Command == "cdt")
19701  {
19702  break; // out of the 'z' loop since the check is only valid up to a change of direction
19703  }
19704  if(AVEntry.LocationName == LocationNameToBeChecked)
19705  {
19706  continue; // keep going while name same
19707  }
19708  if(AVEntry.LocationName != LocationNameToBeChecked)
19709  // if name different check forwards to see if repeats
19710  {
19711  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
19712  {
19713  AnsiString LocationName;
19714  if(TDEntry.ActionVector.at(a).Command == "cdt")
19715  {
19716  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
19717  }
19718  if(TDEntry.ActionVector.at(a).Command == "Fer") //this section where continuation name assigned for Fers added at v2.18.0
19719  {
19720  int ExitLoc = TDEntry.ActionVector.at(a).ExitList.front();
19721  LocationName = Track->TrackElementAt(1679, ExitLoc).ActiveTrackElementName;
19722  }
19723  else
19724  {
19725  LocationName = TDEntry.ActionVector.at(a).LocationName;
19726  }
19727  if(LocationName == LocationNameToBeChecked)
19728  {
19729  SecondInstance = a;
19730  AnsiString Sequence = TDEntry.ServiceReference;
19731  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
19732  {
19733  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
19734  {
19735  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
19736  }
19737  }
19738  if(MissingcdtUnreportedFlag)
19739  {
19740  TTFile3 << "Possibly missing changes of direction - these will be missing unless the service travels in a loop back to the locations marked:-\n\n";
19741  }
19742  TTFile3 << LocationNameToBeChecked << " is listed twice with no direction change between in service sequence: " << Sequence << "\n\n";
19743  MarkerList.push_back(FirstInstance);
19744  MarkerList.push_back(SecondInstance);
19745  SingleServiceOutput(0, x, MarkerList, SingleServiceVector, TTFile3);
19746  MissingcdtUnreportedFlag = false;
19747  FullBreak = true; //no more checks for this sequence
19748  break; //out of the a & z loops
19749  }
19750  }
19751  break; // out of the 'z' loop since have checked 'a' as far as need to
19752  }
19753  }
19754  y++;
19755  }
19756  }
19757  if(MissingcdtUnreportedFlag)
19758  {
19759  TTFile3 << "Nothing to report for missing changes of direction\n\n";
19760  }
19761  else
19762  {
19763  TTFile3 << '\n';
19764  }
19765  SequenceLog += "14\n";
19766 
19767 /* Perform the questionable cdt check. Examine each service in the SingleServiceVector, and if don't find the same
19768  name either side of a cdt (before the next cdt) then flag as a questionable cdt.
19769  Method: Have an outer loop for each service that looks for cdts. When found work backwards to the last cdt or beginning and std::list all the
19770  locations excluding the cdt location. Then work forwards to the next cdt or the end and do the same. Sort each list and make unique (duplicated
19771  names on one side of a cdt already checked either by the tt validator or the missing cdt check). Then compare the two lists and if any location
19772  included in both then ok, else report as questionable. If one list is empty then it is reported.
19773 */
19774  typedef std::list<AnsiString> TLocList;
19775  TLocList BackwardList, ForwardList;
19776  bool IntroLineNeeded = true;
19777  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
19778  {
19779  unsigned int cdtPosition = 9999;
19780  AnsiString cdtLocation = "";
19781  bool FoundSameName = false;
19782  bool FerEntry = false;
19783  MarkerList.clear();
19784  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
19785  for(unsigned int y = 0; y <= TDEntry.ActionVector.size(); y++) // <= because need to examine Fer endings after reached end of vector
19786  // need to check each location name separately in turn, skipped for SignallerControl entries
19787  {
19788  if((y == TDEntry.ActionVector.size()) && !FerEntry)
19789  {
19790  break;
19791  }
19792  BackwardList.clear();
19793  ForwardList.clear();
19794  bool ValidEnd = false;
19795  if(y < TDEntry.ActionVector.size())
19796  {
19797  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
19798  if((AVEntry.FormatType == Repeat) || //end of SSVector, shouldn't be any repeats
19799  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
19800  (AVEntry.Command == "Frh-sh"))
19801  {
19802  ValidEnd = true;
19803  }
19804  }
19805  if(FerEntry || ValidEnd)
19806  {
19807  if(MarkerList.empty())
19808  {
19809  break; // out of the 'y' loop since have reached the end & nothing to report
19810  }
19811  else
19812  {
19813  AnsiString Sequence = TDEntry.ServiceReference;
19814  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
19815  {
19816  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
19817  {
19818  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
19819  }
19820  }
19821  MarkerList.sort();
19822  if(IntroLineNeeded)
19823  {
19824  TTFile3 << "Questionable change of direction analysis.\n\n";
19825  TTFile3 << "For marked changes of direction there are no same-name locations listed both above (up to the start or another direction change)\n";
19826  TTFile3 << "and below (down to the end or another direction change) but not counting the change of direction location itself.\n\n";
19827  TTFile3 << "These changes of direction are probably valid for movements to and from depots but all should be checked to\n";
19828  TTFile3 << "make sure that none has been included incorrectly:\n\n";
19829  IntroLineNeeded = false;
19830  }
19831  TTFile3 << "Service sequence " << Sequence << " contains questionable changes of direction:-\n\n";
19832  SingleServiceOutput(1, x, MarkerList, SingleServiceVector, TTFile3);
19833  break;
19834  }
19835  }
19836  if(y < TDEntry.ActionVector.size()) //if it is == ...size() then shouldn't have reached here
19837  {
19838  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
19839  if(AVEntry.Command != "cdt")
19840  {
19841  continue; //only looking for cdts
19842  }
19843  //here have found a cdt
19844  cdtPosition = y;
19845  cdtLocation = AVEntry.LocationName;
19846  for(int z = y - 1; z >= 0; z--)
19847  {
19848  const TActionVectorEntry &AVEntry2 = TDEntry.ActionVector.at(z);
19849  if(AVEntry2.Command == "cdt")
19850  {
19851  break; //don't look further back than the last cdt
19852  }
19853  AnsiString LocName = ""; //this section where continuation name assigned for unlocated Snts added at v2.18.0
19854  if((AVEntry2.Command == "Snt") && (AVEntry2.LocationType == EnRoute)) //unlocated Snt - check if the continuation has a name
19855  {
19856  int EntryPos = AVEntry2.RearStartOrRepeatMins; //this is a track vector position
19858  }
19859  if(LocName == "")
19860  {
19861  LocName = AVEntry2.LocationName; //the only un-named values for ActionVectorEntry::LocationName
19862  } //are for unlocated Snts and Fers
19863  if((LocName != "") && (AVEntry2.LocationName != cdtLocation)) //if an earlier entry == cdtLocation will have been picked up in an earlier check
19864  {
19865  BackwardList.push_back(LocName);
19866  }
19867  }
19868  BackwardList.sort();
19869  BackwardList.unique();
19870  for(unsigned int z = y + 1; z < TDEntry.ActionVector.size(); z++)
19871  {
19872  const TActionVectorEntry &AVEntry3 = TDEntry.ActionVector.at(z);
19873  if(/*(AVEntry3.Command == "Fer") || */(AVEntry3.FormatType == Repeat) ||
19874  (AVEntry3.Command == "Fjo") || (AVEntry3.Command == "Frh") ||
19875  (AVEntry3.Command == "Frh-sh") || (AVEntry3.Command == "cdt"))
19876  {
19877  break; // out of the 'z' loop since have reached another cdt or the end
19878  }
19879  AnsiString LocName = "";
19880  if(AVEntry3.Command == "Fer") //this section where continuation name assigned for Fers added at v2.18.0
19881  {
19882  int ExitLoc = AVEntry3.ExitList.front();
19883  LocName = Track->TrackElementAt(1681, ExitLoc).ActiveTrackElementName;
19884  FerEntry = true;
19885  }
19886  else
19887  {
19888  LocName = AVEntry3.LocationName;
19889  }
19890  if((LocName != "") && (AVEntry3.LocationName != cdtLocation)) //if a later entry == cdtLocation will have been picked up in an earlier check
19891  {
19892  ForwardList.push_back(LocName);
19893  }
19894  }
19895  ForwardList.sort();
19896  ForwardList.unique();
19897  FoundSameName = false;
19898  //now have both lists compiled (may be empty) so check for same name in both & report if don't find any
19899  if(!BackwardList.empty() && !ForwardList.empty())
19900  {
19901  for(TLocList::iterator BLIt = BackwardList.begin(); BLIt != BackwardList.end(); BLIt++)
19902  {
19903  for(TLocList::iterator FLIt = ForwardList.begin(); FLIt != ForwardList.end(); FLIt++)
19904  {
19905  if(*BLIt == *FLIt)
19906  {
19907  FoundSameName = true;
19908  }
19909  }
19910  }
19911  }
19912  if(!FoundSameName) //report the inability to find same name
19913  {
19914  MarkerList.push_back(cdtPosition);
19915  }
19916  }
19917  }
19918  }
19919  if(IntroLineNeeded)
19920  {
19921  TTFile3 << "Nothing to report for questionable changes of direction\n\n";
19922  }
19923  else
19924  {
19925  TTFile3 << '\n';
19926  }
19927 /*
19928 //debug section
19929 //print all SSVector for diagnostic purposes
19930  TTFile3 << "Whole SSVector\n\n";
19931  TNumList EmptyList;
19932  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
19933  {
19934  SingleServiceOutput(, x, EmptyList, SingleServiceVector, TTFile3);
19935  }
19936 //end of debug section
19937 */
19938  }
19939  SequenceLog += "15\n";
19940  TTFile3.close();
19941  Utilities->CallLogPop(2212);
19942  return(true);
19943  }
19944 
19945  catch(const Exception &e) //non error catch
19946  {
19947  AnsiString TTErrorFileName = "Analysis Error.txt";
19948  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
19949  std::ofstream TTError(TTErrorFileName.c_str());
19950  if(TTError == 0)
19951  {
19952  ShowMessage("Analysis error file failed to open - can't be created");
19953  Utilities->CallLogPop(2233);
19954  return(false);
19955  }
19956  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
19957  TTError << TimeNow.c_str() << '\n' << ArrRange << '\n' << ArrChecked << '\n' << DepRange << '\n' <<
19958  DepChecked << '\n' << AtLocChecked << '\n' << SequenceLog << '\n' << AnsiString(e.Message);
19959 
19960  TTError.close();
19961  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
19962  Utilities->CallLogPop(2226);
19963  return(false);
19964  }
19965 }
19966 
19967 // ---------------------------------------------------------------------------
19968 void TTrainController::SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
19969 {
19970  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(SSVectorNumber) + ',' + ",SingleServiceOutput");
19971  if((SSVectorNumber < 0) || ((unsigned int)SSVectorNumber >= SingleServiceVector.size()))
19972  {
19973  throw Exception("SSVectorNumber out of range, = " + AnsiString(SSVectorNumber) + ", Vector size = " + SingleServiceVector.size());
19974  }
19975  TTrainDataEntry SingleService = SingleServiceVector.at(SSVectorNumber);
19976  {
19977  VecFile << ",Initial service reference " << SingleService.ServiceReference + '\n';
19978  AnsiString Marker = "";
19979  for(unsigned int x = 0; x < SingleService.ActionVector.size(); x++)
19980  {
19981  Marker = ',';
19982  for(TNumListIterator MLIt = MarkerList.begin(); MLIt != MarkerList.end(); MLIt++)
19983  {
19984  if(int(x) == *MLIt)
19985  {
19986  Marker = "-->,";
19987  break;
19988  }
19989  }
19990  TActionVectorEntry AVE = SingleService.ActionVector.at(x);
19991  if(AVE.FormatType == StartNew)
19992  {
19993  AnsiString RearID = Track->TrackElementAt(1397, AVE.RearStartOrRepeatMins).ElementID;
19994  AnsiString FrontID = Track->TrackElementAt(1398, AVE.FrontStartOrRepeatDigits).ElementID;
19995  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << '\n';
19996  }
19997  if(AVE.FormatType == SNTShuttle)
19998  {
19999  AnsiString RearID = Track->TrackElementAt(1399, AVE.RearStartOrRepeatMins).ElementID;
20000  AnsiString FrontID = Track->TrackElementAt(1400, AVE.FrontStartOrRepeatDigits).ElementID;
20001  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << ' ' << AVE.OtherHeadCode << '\n';
20002  }
20003  if(AVE.FormatType == SNSShuttle) //should all have been converted to chr
20004  {
20005  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
20006  }
20007  if(AVE.FormatType == Repeat) //shouldn't be any repeats, only here to show if any have been copied
20008  {
20009  VecFile << Marker << "Repeat " << AVE.RearStartOrRepeatMins << ' ' << AVE.FrontStartOrRepeatDigits << ' ' << AVE.NumberOfRepeats << '\n';
20010  }
20011  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
20012  {
20013  TActionVectorEntry AVHolder = AVE;
20014  if(AVE.Command.SubString(1,3) == "chr")
20015  {
20016  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "sp")
20017  {
20018  AVE.Command = "Change of service to " + AVE.OtherHeadCode + " after split";
20019  AVE.OtherHeadCode = "";
20020  }
20021  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns")
20022  {
20023  AVE.Command = "Change of service to ";
20024  }
20025  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns-sh")
20026  {
20027  AVE.Command = "Change to shuttle finishing service";
20028  }
20029  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "F-nshs")
20030  {
20031  AVE.Command = "Change to shuttle service " + AVE.OtherHeadCode + " from feeder";
20032  AVE.OtherHeadCode = "";
20033  }
20034  }
20035  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << '\n';
20036  AVE = AVHolder;
20037  }
20038  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
20039  {
20040  VecFile << Marker << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
20041  }
20042  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
20043  {
20044  VecFile << Marker << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
20045  }
20046  else if(AVE.FormatType == TimeTimeLoc)
20047  {
20048  VecFile << Marker << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
20049  }
20050  else if(AVE.FormatType == PassTime)
20051  {
20052  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
20053  }
20054  else if(AVE.FormatType == ExitRailway) //ListOfExits added at v2.10.0
20055  {
20056  AnsiString ListOfExits = "";
20057  for(TNumListIterator NLIt = AVE.ExitList.begin(); NLIt != AVE.ExitList.end(); NLIt++)
20058  {
20059  ListOfExits += AnsiString(Track->TrackElementAt(1432, *NLIt).ElementID) + ' ';
20060  }
20061  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << " Fer " << ListOfExits <<'\n';
20062  }
20063  else if(AVE.FormatType == FinRemHere)
20064  {
20065  VecFile << Marker << "Frh" << '\n';
20066  }
20067  }
20068  VecFile << '\n';
20069  }
20070  Utilities->CallLogPop(2318);
20071 }
20072 
20073 // ---------------------------------------------------------------------------
20074 
20075 TTrainDataEntry TTrainController::GetServiceFromVector(AnsiString Caller, AnsiString HeadCode, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
20076 {
20077  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetServiceFromVector," + HeadCode);
20078  FoundFlag = false;
20079  FinishType = true;
20080  for(unsigned int x = 0; x < Vector.size(); x++)
20081  {
20082 // AnsiString ThisRef = Vector.at(x).ServiceReference;
20083  if(Vector.at(x).ServiceReference == HeadCode)
20084  {
20085  if(Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1).FormatType == Repeat) //shouldn't be any repeats
20086  {
20087  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 2);
20088  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
20089  {
20090  FinishType = false;
20091  }
20092  }
20093  else
20094  {
20095  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1);
20096  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
20097  {
20098  FinishType = false;
20099  }
20100  }
20101  FoundFlag = true;
20102  Utilities->CallLogPop(2319);
20103  return(Vector.at(x));
20104  }
20105  }
20106  Utilities->CallLogPop(2320);
20107  return(Vector.at(Vector.size() - 1)); //return last for want of returning something
20108 }
20109 
20110 // ---------------------------------------------------------------------------
20111 
20112 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
20113 {
20114 //convert times to integer minutes
20115  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
20116  if((Time1 == "") || (Time2 == ""))
20117  {
20118  Utilities->CallLogPop(2213);
20119  return(false);
20120  }
20121  int Mins = Time1.SubString(4,2).ToInt();
20122  int Hours = Time1.SubString(1,2).ToInt();
20123  int Time1Mins = (Hours * 60) + Mins;
20124  Mins = Time2.SubString(4,2).ToInt();
20125  Hours = Time2.SubString(1,2).ToInt();
20126  int Time2Mins = (Hours * 60) + Mins;
20127  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
20128  {
20129  Utilities->CallLogPop(2214);
20130  return(true);
20131  }
20132  Utilities->CallLogPop(2215);
20133  return(false);
20134 }
20135 
20136 // ---------------------------------------------------------------------------
20137 
20138 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
20139  bool &AnalysisError, int &MaxNumberOfSameDirections)
20140 {
20141  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
20142 
20143  try
20144  {
20145  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
20146  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
20147  int SCPos = 0;
20148  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
20149  //first change every second comma in Input to a semicolon so can separate services but keep times with services
20150  bool EvenComma = false;
20151  for(int x = 1; x <= Input.Length(); x++)
20152  {
20153  TempStr1 = Input[x];
20154  if(TempStr1 == AnsiString(',') && EvenComma)
20155  {
20156  TempStr2 += ';';
20157  }
20158  else
20159  {
20160  TempStr2 += Input[x];
20161  }
20162  if(TempStr1 == AnsiString(','))
20163  {
20164  EvenComma = !EvenComma;
20165  }
20166  }
20167  //load up the list of services with associated times
20168  while(TempStr2.Length() > 0)
20169  {
20170  SCPos = TempStr2.Pos(';');
20171  if(SCPos > 0) //0 if not found, as won't be when only one service left
20172  {
20173  OneService = TempStr2.SubString(1, SCPos - 1);
20174  ServiceList.push_back(OneService);
20175  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
20176  }
20177  else //no semicolon so looking at last (or only) element
20178  {
20179  ServiceList.push_back(TempStr2);
20180  TempStr2 = "";
20181  }
20182  }
20183  ServiceList.sort(); // alphabetical order
20184  ServiceList.unique(); //remove duplicates
20185  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
20186 
20187  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
20188  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
20189  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
20190  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
20191 
20192  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20193  {
20194  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
20195  }
20196  SLIt3 = ServiceList.end();
20197  SLIt3--; //so points to last element
20198  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
20199  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
20200  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
20201  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
20202  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
20203  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
20204 
20205  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
20206  {
20207  SLIt = SLIt1;
20208  SLIt++; //so points to one after SLIt1
20209  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
20210  {
20211  continue; //already allocated so skip to the next
20212  }
20213  else
20214  {
20215  CommaPos1 = SLIt1->Pos(','); //can't be 0
20216  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
20217  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
20218  SpacePos = ServiceRef1.Pos(' ');
20219  RepeatNum1 = 0;
20220  if(SpacePos > 0) //otherwise it's already correct
20221  {
20222  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
20223  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
20224  if(RepeatInfo1[1] == 'F')
20225  {
20226  RepeatNum1 = 0;
20227  }
20228  else
20229  {
20230  SpacePos = RepeatInfo1.Pos(' ');
20231  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
20232  }
20233  }
20234  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
20235  //but this includes the "&0" etc so need to strip these
20236  AmpersandPos = AnsiTime1.Pos('&');
20237  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
20238 
20239  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
20240  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
20241  {
20242  throw Exception("ASCLIt1 Error in " + Input);
20243  }
20244  ServiceCallingLocsList1 = ASCLIt1->second;
20245  AmpersandPos = SLIt1->Pos('&');
20246  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
20247  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
20248 
20249  SameDirectionCount = 1;
20250  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
20251  {
20252  CommaPos2 = SLIt2->Pos(','); //can't be 0
20253  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
20254  //but this contains "(First service..." etc so need to strip these
20255  SpacePos = ServiceRef2.Pos(' ');
20256  RepeatNum2 = 0;
20257  if(SpacePos > 0) //otherwise it's already correct
20258  {
20259  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
20260  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
20261  if(RepeatInfo2[1] == 'F')
20262  {
20263  RepeatNum2 = 0;
20264  }
20265  else
20266  {
20267  SpacePos = RepeatInfo2.Pos(' ');
20268  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
20269  }
20270  }
20271  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
20272  //but this includes the "&0" etc so need to strip these
20273  AmpersandPos = AnsiTime2.Pos('&');
20274  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
20275 
20276  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
20277  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
20278  {
20279  throw Exception("ASCLIt2 Error in " + Input);
20280  }
20281  ServiceCallingLocsList2 = ASCLIt2->second;
20282  //now compare the two
20283  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
20284  {
20285  int AmpersandPos = SLIt2->Pos('&');
20286  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
20287  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
20288  SameDirectionCount++;
20289  }
20290  }
20291  if(SameDirectionCount > MaxNumberOfSameDirections)
20292  {
20293  MaxNumberOfSameDirections = SameDirectionCount;
20294  }
20295  }
20296  }
20297 
20298  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
20299  {
20300  //any existing direction so allocate it now
20301  AmpersandPos = SLIt3->Pos('&');
20302  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
20303  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
20304  }
20305  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
20306  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20307  {
20308  //extract the DirectionMarker as an integer
20309  AmpersandPos = SLIt->Pos('&');
20310  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
20311  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
20312  DirectionMarker = DirectionMarkerString.ToInt();
20313  AnsiString DirectionSuffix = "";
20314  char c;
20315  if(DirectionMarker < 27)
20316  {
20317  c = 64 + DirectionMarker; //so 1 -> 'A'
20318  DirectionSuffix = "," + AnsiString(c);
20319  }
20320  else if(DirectionMarker < 53)
20321  {
20322  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
20323  DirectionSuffix = ",A" + AnsiString(c);
20324  }
20325  else
20326  {
20327  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
20328  }
20329  *SLIt = ServiceWithoutMarker + DirectionSuffix;
20330  }
20331  //now prepare the final consolidated output
20332  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20333  {
20334  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
20335  }
20336  if(Output.Length() > 0)
20337  {
20338  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
20339  }
20340  Utilities->CallLogPop(2216);
20341  return(Output);
20342  }
20343 
20344  catch(const Exception &e) //non error catch
20345  {
20346  AnalysisError = true;
20347  Utilities->CallLogPop(2227);
20348  return(e.Message);
20349  }
20350 }
20351 
20352 // ---------------------------------------------------------------------------
20353 
20354 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
20355 {
20356  //similar to above but doesn't include times in the input
20357  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
20358  AnsiString InternalInput = Input, Output = "", OneService = "";
20359  int CommaPos = 0;
20360  std::list<AnsiString> ServiceList;
20361  //load up the list
20362  while(InternalInput.Length() > 0)
20363  {
20364  CommaPos = InternalInput.Pos(',');
20365  if(CommaPos > 0) //0 if not found, as won't be when only one service left
20366  {
20367  OneService = InternalInput.SubString(1, CommaPos - 1);
20368  ServiceList.push_back(OneService);
20369  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
20370  }
20371  else //no comma so looking at last (or only) element
20372  {
20373  ServiceList.push_back(InternalInput);
20374  InternalInput = "";
20375  }
20376  }
20377 
20378  ServiceList.sort(); // alphabetical order
20379  ServiceList.unique(); //remove duplicates
20380  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
20381  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20382  {
20383  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
20384  }
20385  if(Output.Length() > 0)
20386  {
20387  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
20388  }
20389  Utilities->CallLogPop(2217);
20390  return(Output);
20391 }
20392 
20393 // ---------------------------------------------------------------------------
20394 
20395 
20396 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
20397  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
20398 {
20399  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
20400  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
20401 
20402  std::list<AnsiString>::iterator LP1, LP2, ListPtr1, ListPtr2, LocPtr1, LocPtr2; //LP1 & 2 are temporary pointers, ListPtrs are
20403  //general list pointers, LocPtrs point to Location in the two lists
20404 
20405  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
20406  //for List1
20407  bool LocFound = false;
20408  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
20409  int IncMinutes;
20410  TDateTime FirstServiceTime;
20411 
20412  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
20413  int Ref1Target = 0, Ref1Count = 0;
20414  int Ref2Target = 0, Ref2Count = 0;
20415 
20416 /* drop this after retained slashes in ServiceRef
20417  int SlashPos = Ref1.Pos('/');
20418  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
20419  {
20420  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
20421  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
20422  }
20423  int Ref2Target = 0, Ref2Count = 0;
20424  SlashPos = Ref2.Pos('/');
20425  if(SlashPos > 0) //if 0 leave as is
20426  {
20427  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
20428  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
20429  }
20430 */
20431 
20432  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
20433  {
20434  //even if others have same names. But if there are cdt's then need to refind the correct service
20435  if((*ListPtr1) == Location) //
20436  {
20437  LocPtr1 = ListPtr1; //may be modified later
20438  LocFound = true;
20439  }
20440  if(ListPtr1->SubString(1, 3) == "%%%")
20441  {
20442  AnsiString CDTTime = ListPtr1->SubString(4, 5);
20443  //now adjust the time to correspond to the repeat if there is one
20444  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
20445  {
20446  IncMinutes = -1;
20447  FirstServiceTime = TDateTime(-1);
20448  bool BreakFlag = false;
20449  for(TTrainDataVector::iterator TDVIt = TrainDataVectorCopy.begin(); TDVIt != TrainDataVectorCopy.end(); TDVIt++)
20450  {
20451  if(TDVIt->ServiceReference == Ref1)
20452  {
20453  if(Ref1Target > Ref1Count)
20454  {
20455  Ref1Count++;
20456  continue;
20457  }
20458  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
20459  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
20460  {
20461  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
20462  {
20463  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
20464  BreakFlag = true;
20465  break;
20466  }
20467  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
20468  {
20469  FirstServiceTime = AVIt->ArrivalTime;
20470  BreakFlag = true;
20471  break;
20472  }
20473  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
20474  {
20475  FirstServiceTime = AVIt->DepartureTime;
20476  BreakFlag = true;
20477  break;
20478  }
20479  }
20480  if(BreakFlag)
20481  {
20482  break;
20483  }
20484  }
20485  }
20486  if(IncMinutes == -1)
20487  {
20488  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20489  }
20490  if(FirstServiceTime == TDateTime(-1))
20491  {
20492  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20493  }
20494  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
20495  }
20496  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
20497  {
20498  LocFound = false;
20499  continue;
20500  }
20501  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
20502  {
20503  break;
20504  }
20505  if(Time1 > CDTTime) //not there yet so go on
20506  {
20507  LocFound = false;
20508  continue;
20509  }
20510  if(Time1 < CDTTime) //gone too far so can stop now
20511  {
20512  break;
20513  }
20514  }
20515  }
20516  if(!LocFound) //have to find it in both lists
20517  {
20518  Utilities->CallLogPop(2228);
20519  return( false);
20520  }
20521  //for List2
20522  LocFound = false;
20523  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
20524  {
20525  if((*ListPtr2) == Location)
20526  {
20527  LocPtr2 = ListPtr2; //may be modified later
20528  LocFound = true;
20529  }
20530  if(ListPtr2->SubString(1, 3) == "%%%")
20531  {
20532  AnsiString CDTTime = ListPtr2->SubString(4, 5);
20533  //now adjust the time to correspond to the repeat if there is one
20534  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
20535  {
20536  IncMinutes = -1;
20537  FirstServiceTime = TDateTime(-1);
20538  bool BreakFlag = false;
20539  for(TTrainDataVector::iterator TDVIt = TrainDataVectorCopy.begin(); TDVIt != TrainDataVectorCopy.end(); TDVIt++)
20540  {
20541  if(TDVIt->ServiceReference == Ref2)
20542  {
20543  if(Ref2Target > Ref2Count)
20544  {
20545  Ref2Count++;
20546  continue;
20547  }
20548  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
20549  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
20550  {
20551  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
20552  {
20553  FirstServiceTime = AVIt->EventTime;
20554  BreakFlag = true;
20555  break;
20556  }
20557  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
20558  {
20559  FirstServiceTime = AVIt->ArrivalTime;
20560  BreakFlag = true;
20561  break;
20562  }
20563  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
20564  {
20565  FirstServiceTime = AVIt->DepartureTime;
20566  BreakFlag = true;
20567  break;
20568  }
20569  }
20570  if(BreakFlag)
20571  {
20572  break;
20573  }
20574  }
20575  }
20576  if(IncMinutes == -1)
20577  {
20578  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20579  }
20580  if(FirstServiceTime == TDateTime(-1))
20581  {
20582  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20583  }
20584  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
20585  }
20586  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
20587  {
20588  LocFound = false;
20589  continue;
20590  }
20591  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
20592  {
20593  break;
20594  }
20595  if(Time2 > CDTTime) //not there yet so go on
20596  {
20597  LocFound = false;
20598  continue;
20599  }
20600  if(Time2 < CDTTime) //gone too far so can stop now
20601  {
20602  break;
20603  }
20604  }
20605  }
20606  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
20607  {
20608  Utilities->CallLogPop(2229);
20609  return( false);
20610  }
20611  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
20612  //set ListPtr1 to the search start position
20613  if(Arrival)
20614  {
20615  LP1 = List1.begin();
20616  LP1--; //now points to before the first entry
20617  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
20618  {
20619  if(ListPtr1 == List1.begin())
20620  {
20621  break;
20622  }
20623  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
20624  {
20625  ListPtr1++; //point to one past the cdt
20626  break;
20627  }
20628  }
20629  //set ListPtr2 to the search start position
20630  LP2 = List2.begin();
20631  LP2--; //now points to before the first entry
20632  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
20633  {
20634  if(ListPtr2 == List2.begin())
20635  {
20636  break;
20637  }
20638  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
20639  {
20640  ListPtr2++; //point to one past the cdt
20641  break;
20642  }
20643  }
20644  //ListPtr1 & 2 now at search start position
20645  LP1 = ListPtr1;
20646  LP2 = ListPtr2;
20647  //now search forwards, i.e. for common locations before Location
20648  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
20649  {
20650  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
20651  {
20652  break;
20653  }
20654  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
20655  {
20656  break;
20657  }
20658  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
20659  {
20660  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
20661  {
20662  break;
20663  }
20664  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
20665  {
20666  break;
20667  }
20668  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
20669  {
20670  Utilities->CallLogPop(2230);
20671  return( true);
20672  }
20673  }
20674  }
20675  }
20676 
20677  //now, for the departure analysis, reset the start positions and search locations after Location
20678 
20679  else
20680  {
20681  LP1 = LocPtr1;
20682  LP1++; //start at one past the location itself
20683  LP2 = LocPtr2;
20684  LP2++;
20685  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
20686  {
20687  if(ListPtr1 == List1.end()) //reached end point so stop
20688  {
20689  break;
20690  }
20691  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
20692  {
20693  break;
20694  }
20695  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
20696  {
20697  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
20698  {
20699  break;
20700  }
20701  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
20702  {
20703  break;
20704  }
20705  if((*ListPtr1) == (*ListPtr2)) //found a common later location
20706  {
20707  Utilities->CallLogPop(2231);
20708  return( true);
20709  }
20710  }
20711  }
20712  }
20713  Utilities->CallLogPop(2232);
20714  return( false);
20715 }
20716 
20717 // ---------------------------------------------------------------------------
20718 
20719 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
20720 {
20721  // changed at v2.7.0 to show allowable exit elements
20722  if(ExitList.empty())
20723  {
20724  return("");
20725  }
20726  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
20727  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
20728  AnsiString ExitLocList = "";
20729  AllowedExits = "";
20730 
20731  unsigned int Counter = 0;
20732  for(TNumListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
20733  {
20734  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
20735  Counter++;
20736  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
20737  {
20738  ExitLocList += "\n";
20739  }
20740  }
20741  if(StartName == "")
20742  {
20743  if(ExitList.size() == 1)
20744  {
20745  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
20746  Utilities->CallLogPop(1571);
20747  return(" at " + ID);
20748  }
20749  else
20750  {
20751  Utilities->CallLogPop(1572);
20752  if(ExitList.size() < 4)
20753  {
20754  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
20755  return("");
20756  }
20757  else
20758  {
20759  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
20760  return("");
20761  }
20762  }
20763  }
20764  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
20765  {
20766  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
20767  {
20768  Utilities->CallLogPop(1570);
20769  if(ExitList.size() < 4)
20770  {
20771  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
20772  return("");
20773  }
20774  else
20775  {
20776  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
20777  return("");
20778  }
20779  }
20780  }
20781  Utilities->CallLogPop(1569);
20782  if(ExitList.size() < 4)
20783  {
20784  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
20785  return(" at " + StartName);
20786  }
20787  else
20788  {
20789  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
20790  return(" at " + StartName);
20791  }
20792 }
20793 
20794 // ---------------------------------------------------------------------------
20795 /* can't trust this as locations within a vector may not be contiguous
20796  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
20797  {
20798  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
20799  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
20800  //must be preceded by a TimeLoc departure
20801  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
20802  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
20803  {
20804  if((AVPtr + x) < TDEPtr->ActionVector.end())
20805  {
20806  AnsiString xx = (AVPtr + x)->Command;//test
20807  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
20808  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
20809  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
20810  {
20811  Utilities->CallLogPop();
20812  return false;
20813  }
20814  else if((AVPtr + x)->SequenceType == FinishSequence)
20815  {
20816  Utilities->CallLogPop();
20817  return true;
20818  }
20819  }
20820  }
20821  Utilities->CallLogPop();
20822  return false;
20823  }
20824 */
20825 // ---------------------------------------------------------------------------
20826 
20827 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
20828 {
20829  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
20830  AnsiString FormatStr = "####0.0";
20831  AnsiString AvLateArrMins = "";
20832  AnsiString AvEarlyArrMins = "";
20833  AnsiString AvLatePassMins = "";
20834  AnsiString AvEarlyPassMins = "";
20835  AnsiString AvLateDepMins = "";
20836  AnsiString AvLateExitMins = "";
20837  AnsiString AvEarlyExitMins = "";
20838 
20839  //calculate remaining CumulativeDelayedRandMinsAllTrains for trains still in vector (CumulativeDelayedRandMinsAllTrains for exited or removed trains already accounted for)
20840  for(unsigned int x = 0; x < TrainVector.size(); x++)
20841  {
20842  Utilities->CumulativeDelayedRandMinsAllTrains += int(TrainVectorAt(89, x).CumulativeDelayedRandMinsOneTrain);
20843  }
20844 
20845  if(LateArrivals > 0)
20846  {
20847  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
20848  }
20849  if(EarlyArrivals > 0)
20850  {
20851  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
20852  }
20853  if(LatePasses > 0)
20854  {
20855  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
20856  }
20857  if(EarlyPasses > 0)
20858  {
20859  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
20860  }
20861  if(LateDeps > 0)
20862  {
20863  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
20864  }
20865  if(LateExits > 0) //added at v2.9.1
20866  {
20867  AvLateExitMins = FormatFloat(FormatStr, (TotLateExitMins / LateExits));
20868  }
20869  if(EarlyExits > 0) //added at v2.9.1
20870  {
20871  AvEarlyExitMins = FormatFloat(FormatStr, (TotEarlyExitMins / EarlyExits));
20872  }
20873  PerfFile << '\n' << '\n' << "***************************************";
20874  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
20875 
20876  if(OnTimeArrivals != 1)
20877  {
20878  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
20879  }
20880  else
20881  {
20882  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
20883  }
20884  if(LateArrivals > 1)
20885  {
20886  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
20887  }
20888  else if(LateArrivals == 1)
20889  {
20890  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
20891  }
20892  else
20893  {
20894  PerfFile << LateArrivals << " late arrivals" << '\n';
20895  }
20896  if(EarlyArrivals > 1)
20897  {
20898  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
20899  }
20900  else if(EarlyArrivals == 1)
20901  {
20902  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
20903  }
20904  else
20905  {
20906  PerfFile << EarlyArrivals << " early arrivals" << '\n';
20907  }
20908  if(OnTimePasses != 1)
20909  {
20910  PerfFile << OnTimePasses << " on-time passes" << '\n';
20911  }
20912  else
20913  {
20914  PerfFile << OnTimePasses << " on-time pass" << '\n';
20915  }
20916  if(LatePasses > 1)
20917  {
20918  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
20919  }
20920  else if(LatePasses == 1)
20921  {
20922  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
20923  }
20924  else
20925  {
20926  PerfFile << LatePasses << " late passes" << '\n';
20927  }
20928  if(EarlyPasses > 1)
20929  {
20930  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
20931  }
20932  else if(EarlyPasses == 1)
20933  {
20934  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
20935  }
20936  else
20937  {
20938  PerfFile << EarlyPasses << " early passes" << '\n';
20939  }
20940 
20941  if(OnTimeExits != 1) //this batch added at v2.9.1
20942  {
20943  PerfFile << OnTimeExits << " on-time exits" << '\n';
20944  }
20945  else
20946  {
20947  PerfFile << OnTimeExits << " on-time exit" << '\n';
20948  }
20949  if(LateExits > 1)
20950  {
20951  PerfFile << LateExits << " late exits (average " << AvLateExitMins.c_str() << " min)" << '\n';
20952  }
20953  else if(LateExits == 1)
20954  {
20955  PerfFile << LateExits << " late exit (" << AvLateExitMins.c_str() << " min)" << '\n';
20956  }
20957  else
20958  {
20959  PerfFile << LateExits << " late exits" << '\n';
20960  }
20961  if(EarlyExits > 1)
20962  {
20963  PerfFile << EarlyExits << " early exits (average " << AvEarlyExitMins.c_str() << " min)" << '\n';
20964  }
20965  else if(EarlyExits == 1)
20966  {
20967  PerfFile << EarlyExits << " early exit (" << AvEarlyExitMins.c_str() << " min)" << '\n';
20968  }
20969  else
20970  {
20971  PerfFile << EarlyExits << " early exits" << '\n';
20972  }
20973 
20974  if(OnTimeDeps != 1)
20975  {
20976  PerfFile << OnTimeDeps << " on-time departures" << '\n';
20977  }
20978  else
20979  {
20980  PerfFile << OnTimeDeps << " on-time departure" << '\n';
20981  }
20982  if(LateDeps > 1)
20983  {
20984  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
20985  }
20986  else if(LateDeps == 1)
20987  {
20988  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
20989  }
20990  else
20991  {
20992  PerfFile << LateDeps << " late departures" << '\n';
20993  }
20994  TDateTime TempExcessLCDownTime;
20995  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
20996  {
20997 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
20998  //later perf summaries with lower values, changed at v2.8.0
20999 // {
21000  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
21001 // }
21002 /*
21003  else
21004  {
21005  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
21006  }
21007 */
21008  if(TempExcessLCDownTime > TDateTime(0))
21009  {
21010  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
21011  }
21012  }
21013 
21014  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
21015 
21016  if(ExcessLCDownMins > 0.1)
21017  {
21018  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
21019  }
21020  else
21021  {
21022  ExcessLCDownMins = 0; //added at v2.16.1 so doesn't count towards performance score if < 0.1mins (else can have low score with no excess mins recorded)
21023  }
21024  if(Utilities->CumulativeDelayedRandMinsAllTrains > 0) //added at v2.13.0
21025  {
21026  PerfFile << Utilities->CumulativeDelayedRandMinsAllTrains << " minutes lost due to random delays when stopped at locations" << '\n';
21027  }
21028  if(MissedStops != 1)
21029  {
21030  PerfFile << MissedStops << " missed stops" << '\n';
21031  }
21032  else
21033  {
21034  PerfFile << MissedStops << " missed stop" << '\n';
21035  }
21036  if(OtherMissedEvents != 1)
21037  {
21038  PerfFile << OtherMissedEvents << " other missed events" << '\n';
21039  }
21040  else
21041  {
21042  PerfFile << OtherMissedEvents << " other missed event" << '\n';
21043  }
21044  if(SkippedTTEvents != 1)
21045  {
21046  PerfFile << SkippedTTEvents << " skipped timetable events" << '\n';
21047  }
21048  else
21049  {
21050  PerfFile << SkippedTTEvents << " skipped timetable event" << '\n';
21051  }
21052  if(UnexpectedExits != 1)
21053  {
21054  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
21055  }
21056  else
21057  {
21058  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
21059  }
21060  if(IncorrectExits != 1)
21061  {
21062  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
21063  }
21064  else
21065  {
21066  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
21067  }
21068  if(NumFailures != 1)
21069  {
21070  PerfFile << NumFailures << " train failures" << '\n';
21071  }
21072  else
21073  {
21074  PerfFile << NumFailures << " train failure" << '\n';
21075  }
21076  if(AvHoursIntValue > 0)
21077  {
21078  if(AvHoursIntValue == 1)
21079  {
21080  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
21081  }
21082  else
21083  {
21084  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
21085  }
21086  }
21087  AnsiString AvLateMinsLocsNotReached = "";
21088 
21090  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
21091  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
21092 
21093  if(LocsNotReached > 0)
21094  {
21095  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
21096  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
21097  }
21098  if(SPADRisks != 1)
21099  {
21100  PerfFile << SPADRisks << " SPAD risks" << '\n';
21101  }
21102  else
21103  {
21104  PerfFile << SPADRisks << " SPAD risk" << '\n';
21105  }
21106  if(SPADEvents != 1)
21107  {
21108  PerfFile << SPADEvents << " SPADs" << '\n';
21109  }
21110  else
21111  {
21112  PerfFile << SPADEvents << " SPAD" << '\n';
21113  }
21114  if(Derailments != 1)
21115  {
21116  PerfFile << Derailments << " derailments" << '\n';
21117  }
21118  else
21119  {
21120  PerfFile << Derailments << " derailment" << '\n';
21121  }
21122  if(CrashedTrains != 1)
21123  {
21124  PerfFile << CrashedTrains << " crashed trains" << '\n';
21125  }
21126  else
21127  {
21128  PerfFile << CrashedTrains << " crashed train" << '\n';
21129  }
21130  PerfFile << '\n' << "***************************************" << '\n';
21131 
21132  bool DerailSPADFlag = false, CrashFlag = false;
21133 
21134  int OverallScorePercent = 100;
21135  int TotArrDepExit = 0;
21136  double TotLateMinsFactor = 1;
21137  double MissedStopAndSPADRiskFactor = 1;
21138  double NetNegFactor = 1;
21139 
21141  EarlyExits + LateExits + OnTimeExits; //exits added at v2.9.1, passes not counted
21142  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
21143  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
21144  // 'no timetabled departures... message, which was inappropriate
21145 
21146  if((SPADEvents > 0) || (Derailments > 0))
21147  {
21148  OverallScorePercent = 5; // overrides other calculations
21149  DerailSPADFlag = true;
21150  }
21151  if(CrashedTrains > 0)
21152  {
21153  OverallScorePercent = 0; // overrides other calculations
21154  CrashFlag = true;
21155  }
21156  if(OverallScorePercent == 100)
21157  {
21158  int LatenessPenalty = TotLateArrMins + TotLateDepMins; //added at v2.13.0 for random delays
21159  if(Utilities->CumulativeDelayedRandMinsAllTrains > LatenessPenalty)
21160  {
21161  LatenessPenalty = 0;
21162  }
21163  else
21164  {
21165  LatenessPenalty -= Utilities->CumulativeDelayedRandMinsAllTrains;
21166  }
21167  if(TotArrDepExit > 0)
21168  {
21169  TotLateMinsFactor = exp((-0.1732) * (LatenessPenalty + OperatingTrainLateMins + NotStartedTrainLateMins + TotLateExitMins +
21170  ((OtherMissedEvents + SkippedTTEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDepExit); //exits added at v2.9.1
21171  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
21172  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
21173  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDepExit);
21174  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
21175  // of arrivals & departures, where 4% = half, 8% = a quarter etc
21176  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
21177  // NetNegfactor: product of the above two
21178  OverallScorePercent = 100 * NetNegFactor;
21179  }
21180  }
21181  if((TotArrDepExit > 0) || DerailSPADFlag || CrashFlag)
21182  // flag condits added at v1.1.4 - see above for what the error was
21183  {
21184  AnsiString OneFailureString = ", though the failure would account for some poor performance";
21185  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
21186  AnsiString AddedString = "";
21187  if(NumFailures == 1)
21188  {
21189  AddedString = OneFailureString;
21190  }
21191  if(NumFailures > 1)
21192  {
21193  AddedString = TwoOrMoreFailureString;
21194  }
21195  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
21196  AnsiString Rating = "";
21197  if(OverallScorePercent == 100)
21198  {
21199  Rating = "Perfect!";
21200  }
21201  else if(OverallScorePercent >= 95)
21202  {
21203  Rating = "Excellent";
21204  }
21205  else if(OverallScorePercent >= 90)
21206  {
21207  Rating = "Very good";
21208  }
21209  else if(OverallScorePercent >= 80)
21210  {
21211  Rating = "Good";
21212  }
21213  else if(OverallScorePercent >= 70)
21214  {
21215  Rating = "Fair";
21216  }
21217  else if(OverallScorePercent >= 60)
21218  {
21219  Rating = "Unacceptable" + AddedString;
21220  }
21221  else if(OverallScorePercent >= 50)
21222  {
21223  Rating = "Poor" + AddedString;
21224  }
21225  else if(OverallScorePercent >= 40)
21226  {
21227  Rating = "Bad" + AddedString;
21228  }
21229  else if(OverallScorePercent >= 30)
21230  {
21231  Rating = "Very bad" + AddedString;
21232  }
21233  else if(OverallScorePercent >= 20)
21234  {
21235  Rating = "Terrible" + AddedString;
21236  }
21237  else if(OverallScorePercent >= 10)
21238  {
21239  Rating = "Appalling" + AddedString;
21240  }
21241  else if(OverallScorePercent >= 5)
21242  {
21243  if(DerailSPADFlag)
21244  {
21245  Rating = "Disastrous - potential loss of life";
21246  }
21247  // SPADs/Derailments
21248  else
21249  {
21250  Rating = "Dire" + AddedString;
21251  }
21252  }
21253  else if(OverallScorePercent < 5)
21254  {
21255  if(CrashFlag)
21256  {
21257  Rating = "Catastrophic - loss of life"; // Crashes
21258  }
21259  else
21260  {
21261  Rating = "Abysmal";
21262  }
21263  }
21264  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
21265  }
21266  else
21267  {
21268  PerfFile << "\nThere were no timetabled departures, arrivals or exits so there is insufficient information to provide a performance score or rating" << '\n';
21269  }
21270  PerfFile << '\n' << "***************************************";
21271  PerfFile.flush();
21272  Utilities->CallLogPop(1736);
21273 }
21274 
21275 // ---------------------------------------------------------------------------
21276 
21278 {
21279  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
21280  for(unsigned int x = 0; x < TrainVector.size(); x++)
21281  {
21282  TTrain &Train = TrainVectorAt(58, x);
21283  if(Train.Crashed)
21284  // can't use background colours for crashed & derailed because same colour
21285  {
21286  CrashWarning = true;
21287  }
21288  else if(Train.Derailed)
21289  // can't use background colours for crashed & derailed because same colour
21290  {
21291  DerailWarning = true;
21292  }
21293  else if(Train.BackgroundColour == clSPADBackground)
21294  // use colour as that changes as soon as passes signal
21295  {
21296  SPADWarning = true;
21297  }
21298  else if(Train.BackgroundColour == clTrainFailedBackground)
21299  {
21300  TrainFailedWarning = true;
21301  }
21302  else if(Train.BackgroundColour == clCallOnBackground)
21303  // use colour as also stopped at signal
21304  {
21305  CallOnWarning = true;
21306  }
21307  else if(Train.BackgroundColour == clSignalStopBackground)
21308  // use colour to distinguish from call-on
21309  {
21310  SignalStopWarning = true;
21311  }
21312  else if(Train.BackgroundColour == clBufferAttentionNeeded)
21313  // use colour to distinguish from ordinary buffer stop
21314  {
21315  BufferAttentionWarning = true;
21316  }
21317  }
21318  Utilities->CallLogPop(1796);
21319 }
21320 
21321 // ---------------------------------------------------------------------------
21322 
21324 {
21325  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
21326 
21327  // calculate lateness for running trains
21330  for(unsigned int x = 0; x < TrainVector.size(); x++)
21331  {
21332  TTrain &Train = TrainVectorAt(64, x);
21333  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
21334  AVEntryPtr++)
21335  {
21336  if(AVEntryPtr < Train.ActionVectorEntryPtr)
21337  {
21338  continue;
21339  }
21340  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.RevisedStoppedAtLoc() && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
21341  TTClockTime))
21342  {
21343  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
21345  }
21346 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
21347  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
21348  TTClockTime))
21349  {
21350  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
21351  OperatingTrainArrDep++;
21352  }
21353 */
21354  }
21355  }
21356 
21357  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
21360 
21361  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
21362  {
21363  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
21364  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
21365  int IncrementalMinutes = 0;
21366  if(AVEntryLast.FormatType == Repeat)
21367  {
21368  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
21369  }
21370  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
21371  {
21372  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
21373  if(TTOD.RunningEntry != NotStarted)
21374  {
21375  continue;
21376  }
21377  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
21378  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
21379  bool TrainOperatingFlag = false;
21380  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
21381  {
21382  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
21383  {
21384  TrainOperatingFlag = true;
21385  break;
21386  }
21387  }
21388  if(TrainOperatingFlag)
21389  {
21390  continue;
21391  }
21392  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
21393  {
21394  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
21395  }
21396  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
21397  {
21398  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
21399  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
21400  {
21401  break; // all the rest will also be greater (& default of -1 will be less)
21402  }
21403  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
21404  {
21405  break; // all the rest will also be greater (& default of -1 will be less)
21406  }
21407  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
21408  {
21409  break; // all the rest will also be greater (& default of -1 will be less)
21410  }
21411  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
21412  {
21413  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
21415  }
21416 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
21417  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
21418  {
21419  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
21420  NotStartedTrainArrDep++;
21421  }
21422 */
21423  }
21424  }
21425  }
21426  Utilities->CallLogPop(1894);
21427 }
21428 
21429 // ---------------------------------------------------------------------------
21430 
21432 // new v2.2.0 for OperatorActionPanel (OperatorActionPanel changed for ActionsDueForm at v2.13.0)
21433 // clears entries then adds values for running trains then for continuation entries
21434 // dont limit size here as need to check all trains (ActionsDueListBox is limited to 20 trains in Interface.cpp)
21435 {
21436  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
21437  OpTimeToActMultiMap.clear();
21438  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
21439 
21440  if(!TrainVector.empty())
21441  // build OpTimeToActMultiMap entries for running trains
21442  {
21443  AnsiString HeadCode;
21444  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
21445  int TrainID;
21446  THCandTrainPosParam HCandTrainPosParam;
21447  for(unsigned int x = 0; x < TrainVector.size(); x++)
21448  {
21449  HeadCode = TrainVectorAt(62, x).HeadCode;
21450  TrainID = TrainVectorAt(63, x).TrainID;
21451  HCandTrainPosParam.first = HeadCode;
21452  HCandTrainPosParam.second = TrainID;
21453  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
21454  if((TimeToAct >= 0) && (TimeToAct < 59.9))
21455  // -1 indicates don't display
21456  {
21457  OpTimeToActMultiMapEntry.first = TimeToAct;
21458  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
21459  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
21460  }
21461  }
21462  }
21463 /*
21464  * class TContinuationTrainExpectationEntry
21465  {
21466  public:
21467  AnsiString Description; ///< service description
21468  AnsiString HeadCode; ///< service headcode
21469  int RepeatNumber; ///< service RepeatNumber
21470  int IncrementalMinutes; ///< Repeat separation in minutes
21471  int IncrementalDigits; ///< Repeat headcode separation
21472  int VectorPosition; ///< TrackVectorPosition for the continuation element
21473  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
21474  };
21475 
21476  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
21477  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
21478  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
21479  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
21480 */
21481 
21483  // build OpTimeToActMultiMap entries for expected trains
21484  {
21485  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
21486  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
21487  float TimeToAct = 0; // minutes
21488  int DistanceToRedSignal = 0; // metres
21489  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
21490  // used to ensure only one train displayed for a given continuation
21491  ContinuationEntryVecPosVector.clear();
21492  bool LaterTrain = false;
21495  {
21496  LaterTrain = false;
21497  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
21498  {
21499  CTEIt++;
21500  continue; // not interested in running or exited trains
21501  }
21502  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
21503  {
21504  CTEIt++;
21505  continue;
21506  // don't include trains not entered yet when a train is already on the continuation
21507  }
21508  if(!ContinuationEntryVecPosVector.empty())
21509  {
21510  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
21511  {
21512  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
21513  {
21514  LaterTrain = true;
21515  ;
21516  // skip past remaining trains waiting to enter at same point
21517  break;
21518  }
21519  }
21520  }
21521  if(LaterTrain)
21522  {
21523  CTEIt++;
21524  continue;
21525  }
21526  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
21527  AnsiString HeadCode = CTEIt->second.HeadCode;
21528  float CurrentStopTime; // set to 0 at start of function
21529  float LaterStopTime; // set to 0 at start of function
21530  float RecoverableTime; // set to 0 at start of function
21531  int AvTrackSpeed; // set to 0 at start of function
21532  int TrainID = -1; // not yet allocated for train still to enter
21533  int DistanceToExit; //not used for continuation entries
21534  THVShortPair ExitPair;
21535  bool SigControlAndCanPassRedSignal = false; // doesn't apply for a continuation
21536 
21537 //at v2.11.0 found that with *AVPtr set to ...ActionVector.at(0) below instead of ...at(1) to stop signaller control trains throwing an error (because there is no ...at(1) -
21538 //discovered with Birmingham) the LaterStopTime isn't calculated and if a train does something other than depart after an arrival it is still listed in the actions due panel -
21539 //because it just calcs the distance to the red signal and converts that to a time. So here (after v2.11.0) this new test is introduced to determine whether a train is a
21540 //signaller control train (when the ActionVector size is 1) or not (ActionVector size > 1), and the ...at(value) is set accordingly - 0 for signaller control or 1 if not.
21541 
21542  int AtValue = 1;
21543  if(CTEIt->second.TrainDataEntryPtr->ActionVector.size() == 1)
21544  {
21545  AtValue = 0;
21546  }
21547  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
21548  // EntryPos always 0 for entering at a continuation
21549  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(AtValue), //see above note
21550  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
21551  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
21552  // for a train it's the one in front of LeadElement
21553  if(AvTrackSpeed < 30)
21554  {
21555  AvTrackSpeed = 30;
21556  }
21557  if(DistanceToRedSignal == -1)
21558  {
21559  TimeToAct = 60.0;
21560  }
21561  else
21562  {
21563  int Speed = AvTrackSpeed;
21564  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
21565  if(AvTrackSpeed > MaxSpeed)
21566  {
21567  Speed = MaxSpeed;
21568  }
21569  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(0).SignallerControl) //changed to ...at(0) from at(1) at v2.11.0 as SignallerControl only valid for ..at(0)
21570  // defined in timetable as under signaller control
21571  {
21572  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
21573  LaterStopTime = 0;
21574  }
21575  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
21576  // accel & decel taken into account in
21577  // CalcDistanceToRedSignalandStopTime
21578  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
21579  // don't need CurrentStopTime or RecoverableTime for continuation entries
21580  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
21581  TimeToAct += MinsBefEnter;
21582  }
21583  THCandTrainPosParam HCandTrainPosParam;
21584  HCandTrainPosParam.first = HeadCode;
21585  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
21586  // -1-CTE... because 2nd value covers TrainID if +ve &
21587  // continuation track vector position if -ve, -1 allows for vecpos being 0
21588  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
21589  {
21590  OpTimeToActMultiMapEntry.first = TimeToAct;
21591  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
21592  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
21593  }
21594  CTEIt++;
21595  }
21596  }
21597  Utilities->CallLogPop(2081);
21598 }
21599 
21600 // ---------------------------------------------------------------------------
21601 
21603 // new for multiplayer
21604 // clears entries then adds values for running trains
21605 {
21606  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildTimeToExitMultiMap");
21607  TimeToExitMultiMap.clear();
21608  TTimeToExitMultiMapEntry TimeToExitMultiMapEntry;
21609 
21610  if(!TrainVector.empty())
21611  // build map entries for running trains
21612  {
21613  TExitInfo ExitInfo; //corresponds to TServiceInfo in Interface
21614  THVShortPair ExitPair;
21615  float TimeToExit;
21616  for(unsigned int x = 0; x < TrainVector.size(); x++)
21617  {
21619  ExitInfo.RepeatNumber = short(TrainVectorAt(81, x).RepeatNumber);
21620  ExitInfo.TimeToExitSecs = short(TrainVectorAt(77, x).TimeToExit * 60);
21621  ExitPair = TrainVectorAt(76, x).ExitPair;
21622  if((ExitInfo.TimeToExitSecs >= 3570) || (ExitInfo.TimeToExitSecs < 1)) //59.5 mins or -60 secs
21623  {
21624  ExitInfo.TimeToExitSecs = -1;
21625  }
21626  TimeToExitMultiMapEntry.first = ExitPair;
21627  TimeToExitMultiMapEntry.second = ExitInfo;
21628  TimeToExitMultiMap.insert(TimeToExitMultiMapEntry);
21629  }
21630  }
21631  Utilities->CallLogPop(2323);
21632 }
21633 
21634 // ---------------------------------------------------------------------------
21635 
21636 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
21637  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
21638  float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
21639 // new v2.2.0
21640 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
21641 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
21642 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
21643 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
21644 // aren't used - this means there is no display for the train in question
21645 {
21646  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
21647  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
21648  int DistanceToRedSignal = 0;
21649  DistanceToExit = -1;
21650  ExitPair.first = -1;
21651  ExitPair.second = -1;
21652  int CumTrackSpeed = 0;
21653  // average track speed, in case need to use in time calc
21654  int TrackSpeedCount = 0;
21655  float KmPerLocationStop;
21656  float MaxAllowableSpeed;
21657 
21658  //below added at v2.6.1
21659  if(TrainID > -1) //will be -1 for trains not entered yet
21660  {
21661  TTrain &Train = TrainVectorAtIdent(51, TrainID);
21662  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
21663  Train.StationStopCalculated = false;
21664  }
21665  AvTrackSpeed = 0;
21666  int CurrentElement = TrackVectorPosition;
21667  int CurrentEntryPos = TrackVectorPositionEntryPos;
21668  int NextElement;
21669  int NextEntryPos;
21670  int NextExitPos;
21671 
21672  CurrentStopTime = 0;
21673  LaterStopTime = 0;
21674  RecoverableTime = 0;
21675  if(CurrentElement == -1) // train on end element, no action needed
21676  {
21677  Utilities->CallLogPop(2094);
21678  return(-1);
21679  }
21680  int CurrentExitPos;
21681 
21682  // get ExitPos for first element to be measured
21683  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
21684  {
21685  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
21686  {
21687  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
21688  {
21689  CurrentExitPos = 1;
21690  }
21691  else
21692  {
21693  CurrentExitPos = 3;
21694  }
21695  }
21696  else
21697  {
21698  CurrentExitPos = 0; // trailing point
21699  }
21700  }
21701  else
21702  {
21703  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
21704  }
21705  // get CumTrackSpeed for first measured element
21706 
21707  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
21708  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
21709  bool CurrentElementFailed = Track->TrackElementAt(1549, CurrentElement).Failed; //added at v2.13.2
21710 
21711  // check if currently stopped at a location, and if so add the remaining dwell time
21712  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
21713  if(TrainID > -1)
21714  // -1 for a continuation and can't be at a location as not yet entered
21715  {
21716  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
21718  // this used to deduct from RecoverableTime when arrive at a location
21719  if(Train.RevisedStoppedAtLoc())
21720  {
21721  if(Train.StoppedForTrainInFront || Train.TrainInFront)
21722  {
21723  Utilities->CallLogPop(2082);
21724  return(-1); // no action needed
21725  }
21727  { //added '|| (Train.ActionVectorEntryPtr->FormatType == TimeCmdDescription)' at v2.16.1 so description ignored in calculating action due time
21728  Utilities->CallLogPop(2083);
21729  return(-1); // not due a departure or a description change so no action needed
21730  }
21731  else if(((Train.ActionVectorEntryPtr + 1)->FormatType == TimeLoc) && (Train.ActionVectorEntryPtr->FormatType == TimeCmdDescription)) // due a departure immediately after change of description
21732  { //added at v2.16.1 to cover description change due next then a departure
21733  double TimeToDepart = double((Train.GetTrainTime(68, (Train.ActionVectorEntryPtr + 1)->DepartureTime)) - TrainController->TTClockTime) * 86400 / 60; // mins to depart excluding possible 30sec allowance from LastActionTime
21734  //need repeat time for the above
21735  if((Train.ActionVectorEntryPtr + 1)->DepartureTime == Train.ActionVectorEntryPtr->EventTime) //don't need repeat time here
21736  {
21737  TimeToDepart+= 0.5; //add in the 30 secs if depature time same as description change time
21738  }
21739  if(TimeToDepart < 0.5)
21740  {
21741  TimeToDepart = 0.5;
21742  }
21743  // can't convert a TDateTime to a float directly
21744  CurrentStopTime = float(TimeToDepart);
21745  AVPtr++;
21746  AVPtr++;
21747  }
21748  else if((Train.ActionVectorEntryPtr->FormatType == TimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc)) // due a departure as next action
21749  {
21750  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
21751  // can't convert a TDateTime to a float directly
21752  CurrentStopTime = float(TimeToDepart);
21753  AVPtr++;
21754  }
21755  else //added at v2.16.1 to catch all other combinations
21756  { //none of the above so no action needed
21757  Utilities->CallLogPop(2628);
21758  return(-1);
21759  }
21760  }
21761  }
21762  // check if CurrentElement is a red signal, but ok if autosig route after provided signal not failed
21763  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
21764  // ok if autosig route after red signal unless signal has failed
21765  {
21766  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
21767  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
21768  int RouteNumber; // holder for referenced value, not used
21769  if((AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !CurrentElementFailed)
21770  { //CurrentElementFailed added at v2.13.2
21771  Utilities->CallLogPop(2078);
21772  return(-1);
21773  }
21774  else if(SigControlAndCanPassRedSignal)
21775  // ignore signal and increment CurrentElement to NextElement
21776  {
21777  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
21778  {
21779  if((NextEntryPos == 0) || (NextEntryPos == 2))
21780  // leading entry point
21781  {
21782  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
21783  {
21784  NextExitPos = 1;
21785  }
21786  else
21787  {
21788  NextExitPos = 3;
21789  }
21790  }
21791  else
21792  {
21793  NextExitPos = 0; // trailing entry point
21794  }
21795  }
21796  else
21797  {
21798  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
21799  }
21800  CurrentElement = NextElement;
21801  CurrentEntryPos = NextEntryPos;
21802  CurrentExitPos = NextExitPos;
21803  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
21804  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
21805  }
21806  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
21807  // give 'NOW' indication after allowed to pass stop signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
21808  {
21809  Utilities->CallLogPop(2084);
21810  return(0);
21811  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
21812  }
21813  }
21814  int LaterStopNumber = 0;
21815  int x = 0;
21816  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
21817 
21818  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
21819  // not red signal next (in fwd direction) so enter loop to calc CumLength
21820  {
21821  x++; // added in v2.4.0 as above
21822  if(x > 5000)
21823  {
21824  Utilities->CallLogPop(2120);
21825  return(-1);
21826  }
21827  if(CurrentEntryPos > 1)
21828  {
21829  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
21830  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
21831  }
21832  else
21833  {
21834  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
21835  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
21836  }
21837  TrackSpeedCount++;
21838 
21839  //added for multiplayer - exiting at a continuation and continuation length already added
21840  if((Track->TrackElementAt(1407, CurrentElement).TrackType == Continuation) && (Track->TrackElementAt(1408, CurrentElement).Config[CurrentExitPos] == End))
21841  {
21842  DistanceToExit = DistanceToRedSignal; //don't need to exit function here as will exit when find that the next Conn value is -1
21843  ExitPair.first = Track->TrackElementAt(1409, CurrentElement).HLoc;
21844  ExitPair.second = Track->TrackElementAt(1410, CurrentElement).VLoc;
21845  //here repeat calcs for MaxAllowableSpeed & AvTrackSpeed as done at end for stop signal
21846  //need here as next element will be -1 so will exit before calcs at end
21847  if(TrackSpeedCount > 0)
21848  {
21849  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
21850  }
21851  else // shouldn't reach here but include to prevent divide by zero error
21852  {
21853  if(CurrentEntryPos > 1)
21854  {
21855  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
21856  }
21857  else
21858  {
21859  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
21860  }
21861  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
21862  }
21863  //calc AvTrackSpeed
21864  if(LaterStopNumber > 0)
21865  {
21866  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
21867  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
21868  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
21869  // average line speed/2 (for half distance accelerating and half decelerating.
21870  }
21871  else
21872  {
21873  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
21874  // using linear trendline for accel & decel distance at various speeds
21875  // at half braking, speed never < 60 using this
21876  }
21877  if(AvTrackSpeed > MaxAllowableSpeed)
21878  {
21879  AvTrackSpeed = MaxAllowableSpeed;
21880  }
21881  }
21882 
21883  // added at v2.6.1 to find DistanceToStationStop for trains running early
21884  if(TrainID > -1) //can ignore continuation entries as these don't run early
21885  {
21886  TTrain &Train = TrainVectorAtIdent(52, TrainID);
21887  if(!Train.StationStopCalculated)
21888  {
21889  if(Train.TrainMode == Timetable)
21890  {
21891  bool StopRequired = false;
21892  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
21893  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
21894  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos) ||
21895  (Track->TrackElementAt(1654, CurrentElement).StationEntryStopLinkPos3 == CurrentEntryPos) ||
21896  (Track->TrackElementAt(1655, CurrentElement).StationEntryStopLinkPos4 == CurrentEntryPos)))
21897  {
21898  // no need to add in the length of element to CumulativeLength
21899  if(StopRequired)
21900  {
21901  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
21902  Train.StationStopCalculated = true; //don't want to update it with later stops
21903  }
21904  }
21905  }
21906  }
21907  }
21908  // check for train in front, but if on a bridge on other track then ok
21909  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
21910  int TrainOnElement;
21911  if(TE.TrackType != Bridge)
21912  {
21913  TrainOnElement = TE.TrainIDOnElement;
21914  }
21915  else
21916  {
21917  if(CurrentEntryPos > 1)
21918  {
21920  }
21921  else
21922  {
21924  }
21925  }
21926  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
21927  // train in front before red signal
21928  {
21929  Utilities->CallLogPop(2085);
21930  return(-1);
21931  }
21932  // add to stoptime if required
21933  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
21934  {
21935  double StopTimeDouble;
21936  while(AVPtr->FormatType == PassTime)
21937  {
21938  AVPtr++; // skip past any passes
21939  }
21940  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
21941  (AVPtr->FormatType == TimeTimeLoc)))
21942  // stop due here so calc dwell time & advance Ptr
21943  {
21944  if(AVPtr->FormatType == TimeTimeLoc)
21945  {
21946  LaterStopNumber++;
21947  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
21948  if(StopTimeDouble < 0.5)
21949  {
21950  StopTimeDouble = 0.5;
21951  }
21952  // at least 30 secs delay at station
21953  // can't convert a TDateTime to a float directly
21954  LaterStopTime += float(StopTimeDouble);
21955  RecoverableTime += StopTimeDouble - 0.5;
21956  if((LaterStopNumber == 1) && (TrainID > -1))
21957  {
21958  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
21959  }
21960  AVPtr++;
21961  }
21962  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
21963  {
21964  if((AVPtr + 1)->FormatType == TimeLoc)
21965  // must be a departure
21966  {
21967  LaterStopNumber++;
21968  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0; //diff will be same for all repeats
21969  // can't convert a TDateTime to a float directly //so repeat times not required
21970  if(TrainID > -1) //exclude trains still to enter
21971  {
21972  TTrain &Train = TrainVectorAtIdent(67, TrainID);
21973  if(TTClockTime > Train.GetTrainTime(69, AVPtr->ArrivalTime)) //running late, added at v2.16.1
21974  {
21975  StopTimeDouble = double(Train.GetTrainTime(70, (AVPtr + 1)->DepartureTime) - TTClockTime) * 86400.0 / 60.0; //may be -ve but if so it's set to 0.5 later
21976  // can't convert a TDateTime to a float directly
21977  }
21978  }
21979  if(StopTimeDouble < 0.5)
21980  {
21981  StopTimeDouble = 0.5;
21982  }
21983  // at least 30 secs delay at station
21984  LaterStopTime += float(StopTimeDouble);
21985  RecoverableTime += StopTimeDouble - 0.5;
21986  if((LaterStopNumber == 1) && (TrainID > -1))
21987  {
21988  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
21989  }
21990  AVPtr++;
21991  AVPtr++;
21992  }
21993  else if(((AVPtr + 1)->FormatType == TimeCmdDescription) && ((AVPtr + 2)->FormatType == TimeLoc)) //change of description then departure
21994  { //added at v2.16.1 so description changes ignored in calculating time to act
21995  LaterStopNumber++;
21996  StopTimeDouble = double((AVPtr + 2)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0; //diff will be same for all repeats
21997  // can't convert a TDateTime to a float directly //so repeat times not required
21998  if(TrainID > -1) //exclude trains still to enter
21999  {
22000  TTrain &Train = TrainVectorAtIdent(68, TrainID);
22001  if(TTClockTime > Train.GetTrainTime(71, AVPtr->ArrivalTime)) //running late, added at v2.16.1
22002  {
22003  StopTimeDouble = double(Train.GetTrainTime(72, (AVPtr + 2)->DepartureTime) - TTClockTime) * 86400.0 / 60.0; //may be -ve but if so it's set to 0.5 later
22004  // can't convert a TDateTime to a float directly
22005  }
22006  }
22007  if(StopTimeDouble < 0.5)
22008  {
22009  StopTimeDouble = 0.5;
22010  }
22011  // at least 30 secs delay at station
22012  LaterStopTime += float(StopTimeDouble);
22013  RecoverableTime += StopTimeDouble - 0.5;
22014  if((LaterStopNumber == 1) && (TrainID > -1))
22015  {
22016  TrainVectorAtIdent(69, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
22017  }
22018  AVPtr++;
22019  AVPtr++;
22020  AVPtr++;
22021  }
22022  else // does something else at the location so no calculation needed
22023  {
22024  Utilities->CallLogPop(2086);
22025  return(-1);
22026  }
22027  }
22028  }
22029  }
22030  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
22031  if(NextElement == -1) // reached end element, no action needed
22032  {
22033  Utilities->CallLogPop(2077);
22034  return(-1);
22035  }
22036  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
22037  // get NextExitPos
22038  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
22039  {
22040  if((NextEntryPos == 0) || (NextEntryPos == 2))
22041  // leading entry point
22042  {
22043  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
22044  {
22045  NextExitPos = 1;
22046  }
22047  else
22048  {
22049  NextExitPos = 3;
22050  }
22051  }
22052  else
22053  {
22054  NextExitPos = 0; // trailing entry point
22055  }
22056  }
22057  else
22058  {
22059  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
22060  }
22061  CurrentElement = NextElement;
22062  CurrentEntryPos = NextEntryPos;
22063  CurrentExitPos = NextExitPos;
22064  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
22065  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
22066  CurrentElementFailed = Track->TrackElementAt(1550, CurrentElement).Failed; //added at v2.13.2
22067  }
22068  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
22069  // ok if autosig route after red signal, no action needed
22070  {
22071  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
22072  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
22073  int RouteNumber; // holder for referenced value, not used
22074  if((AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !CurrentElementFailed)
22075  { //CurrentElementFailed added at v2.13.2
22076  Utilities->CallLogPop(2095);
22077  return(-1);
22078  }
22079  }
22080 
22081  if(TrackSpeedCount > 0)
22082  {
22083  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
22084  }
22085  else // shouldn't reach here but include to prevent divide by zero error
22086  {
22087  if(CurrentEntryPos > 1)
22088  {
22089  MaxAllowableSpeed = Track->TrackElementAt(1433, CurrentElement).SpeedLimit23;
22090  }
22091  else
22092  {
22093  MaxAllowableSpeed = Track->TrackElementAt(1434, CurrentElement).SpeedLimit01;
22094  }
22095  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
22096  }
22097 
22098  if(LaterStopNumber > 0)
22099  {
22100  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
22101  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
22102  }
22103  else
22104  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
22105  // average line speed/2 (for half distance accelerating and half decelerating.
22106  {
22107  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
22108  // using linear trendline for accel & decel distance at various speeds
22109  // at half braking, speed never < 60 using this
22110  }
22111  if(AvTrackSpeed > MaxAllowableSpeed)
22112  {
22113  AvTrackSpeed = MaxAllowableSpeed;
22114  }
22115  Utilities->CallLogPop(2096);
22116  return(DistanceToRedSignal);
22117 }
22118 
22119 // ---------------------------------------------------------------------------
22120 // end of TTrainController entries
22121 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:9093
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:19495
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:133
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:380
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:51
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:15818
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:348
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:347
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:493
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:705
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:72
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1742
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:904
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:171
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:15480
TTrainDataEntry::FixedDescription
AnsiString FixedDescription
Definition: TrainUnit.h:206
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller, bool NoLogFlag)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6186
TActionVectorEntry::SplitDistribution
AnsiString SplitDistribution
Definition: TrainUnit.h:121
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:368
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel (OperatorActionPanel changed for ActionsDueForm at v2....
Definition: TrainUnit.cpp:21431
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:815
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:16922
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:92
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:52
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:1017
Create
@ Create
Definition: TrainUnit.h:51
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:792
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:20502
Arrive
@ Arrive
Definition: TrainUnit.h:51
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:51
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:346
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:386
Depart
@ Depart
Definition: TrainUnit.h:51
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:721
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:12194
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:719
PerfLogForm
TPerfLogForm * PerfLogForm
Definition: PerfLogUnit.cpp:11
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:292
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:430
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:804
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:287
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7480
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:52
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:40
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:910
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1671
IntermediateSequence
@ IntermediateSequence
Definition: TrainUnit.h:77
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:730
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:139
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:786
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1683
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:67
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:66
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:510
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:753
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:67
TTrain::CoastingBrakeRate
double CoastingBrakeRate
the train brake rate when coasting
Definition: TrainUnit.h:438
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:434
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:71
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:21263
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:14713
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Definition: TrackUnit.h:153
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:368
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:1003
TTrain::TrainFailed
bool TrainFailed
Definition: TrainUnit.h:414
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
TTrack::TTrackVectorIterator
std::vector< TTrackElement >::iterator TTrackVectorIterator
iterator for TTrackVector
Definition: TrackUnit.h:652
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:986
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:40
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:801
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:342
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:982
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:20719
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:7969
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:388
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:482
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:368
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6317
TTrackElement::StationEntryStopLinkPos3
int StationEntryStopLinkPos3
Definition: TrackUnit.h:153
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:801
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:10378
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:323
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:901
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route
Definition: TrainUnit.h:727
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:16653
TExitInfo::TimeToExitSecs
short TimeToExitSecs
Definition: TrainUnit.h:107
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:859
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:1009
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:826
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:1016
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:185
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:20291
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:801
TTrainController::ControllerGetNewServiceDepartureInfo
AnsiString ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::GetNewServiceDepartureInfo for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:10573
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:12144
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1663
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:9065
TNumListIterator
TNumList::iterator TNumListIterator
Definition: TrainUnit.h:95
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:42
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:133
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:969
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:41
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:749
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:16668
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:264
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:967
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6109
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:42
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:983
TTrainController::GetServiceFromVector
TTrainDataEntry GetServiceFromVector(AnsiString Caller, AnsiString HeadCode, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
Return the TrainDataVector entry corresponding to ServiceReference, FinishType is 0 for end of servic...
Definition: TrainUnit.cpp:20075
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3593
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:687
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:66
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:506
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:464
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:10340
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:53
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:499
NotStarted
@ NotStarted
Definition: TrainUnit.h:88
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:11513
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:853
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:795
TTrack::OneStationLongEnoughForSplit
bool OneStationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10987
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:349
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:724
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:374
TTrainDataEntry
Contains all data for a single timetable service entry.
Definition: TrainUnit.h:204
LeadMid
@ LeadMid
Definition: TrainUnit.h:297
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:41
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:300
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:843
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:20776
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:15564
TTrack::ResetAllTrainIDsAndFailedPointOrigSpeedLimits
void ResetAllTrainIDsAndFailedPointOrigSpeedLimits(int Caller)
Definition: TrackUnit.cpp:7852
TTrainController::RebuildTimeToExitMultiMap
void RebuildTimeToExitMultiMap(int Caller)
new for multiplayer
Definition: TrainUnit.cpp:21602
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:981
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6338
TOneRoute::RouteID
int RouteID
the ID number of the route, this is needed for session saves
Definition: TrackUnit.h:1558
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:745
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:1023
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:251
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:1019
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:214
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:863
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:820
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:735
TTrain::SkippedDeparture
bool SkippedDeparture
< used for terminating a service early and becoming new follow-on service
Definition: TrainUnit.h:330
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:9542
ChangeDescription
@ ChangeDescription
Definition: TrainUnit.h:54
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:16771
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:39
MidLag
@ MidLag
Definition: TrainUnit.h:297
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:222
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:900
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:16709
StartNew
@ StartNew
Definition: TrainUnit.h:66
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:51
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:297
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:808
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:350
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:52
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:452
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:732
TTrainController::TimeToExitMultiMap
TTimeToExitMultiMap TimeToExitMultiMap
Map of times to exit & exit coordinates.
Definition: TrainUnit.h:1005
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:20039
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:11668
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:992
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5960
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:513
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:183
TTrain
Definition: TrainUnit.h:303
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:989
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:11687
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:299
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:12249
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:42
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:895
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:249
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:877
TPerfLogForm::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: PerfLogUnit.cpp:32
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:488
GapJump
@ GapJump
Definition: TrackUnit.h:66
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:1010
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:846
NoSequence
@ NoSequence
Definition: TrainUnit.h:77
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:293
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:151
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:151
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:410
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:6060
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:9971
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:368
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1710
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:368
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:295
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:362
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:224
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:708
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:366
TExitInfo::TExitInfo
TExitInfo()
Definition: TrainUnit.cpp:63
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:16750
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:839
TTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:326
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:175
TTrain::TrainInFront
bool TrainInFront
Definition: TrainUnit.h:489
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:790
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:40
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:468
TExitInfo::RepeatNumber
short RepeatNumber
Definition: TrainUnit.h:106
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:723
TTrain::LastSigPassedFailed
bool LastSigPassedFailed
flag used to erase route elements in an autosigs route after a failed signal
Definition: TrainUnit.h:396
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:16473
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1416
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:6248
End
@ End
Definition: TrackUnit.h:76
TUtilities::FixedMinRepairTime
int FixedMinRepairTime
Definition: Utilities.h:72
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:400
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:6973
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:9054
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:344
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:10952
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:67
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:305
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:406
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:16833
FailMissedDSC
@ FailMissedDSC
Definition: TrainUnit.h:40
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:125
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:977
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:41
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:788
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:1027
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:304
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:851
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:840
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:424
SignalPost
@ SignalPost
Definition: TrackUnit.h:66
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:971
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled event, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:472
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:144
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1687
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:352
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:41
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:12837
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:999
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:206
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5695
TUtilities::MinorDelayFactor
float MinorDelayFactor
Definition: Utilities.h:53
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:298
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool SetDataAndCheckLocations, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:15841
TTrainController::CheckFourthValidityForSplit
bool CheckFourthValidityForSplit(AnsiString SplitDistributionString, bool GiveMessages)
Checks fourth segment in timetable for train splits - percentage mass then '-' then percentage power ...
Definition: TrainUnit.cpp:12099
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:147
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:854
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:776
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:904
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:422
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:75
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:484
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:65
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:849
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:815
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:898
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1927
TExitInfo
Definition: TrainUnit.h:103
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:291
TUtilities::ModerateDelayCutoff
float ModerateDelayCutoff
Definition: Utilities.h:51
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:488
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:39
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:847
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:305
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:987
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:296
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:66
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:94
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:133
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:488
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:17649
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:965
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:462
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:990
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:10939
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:747
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:368
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:460
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:81
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:1015
Pass
@ Pass
Definition: TrainUnit.h:53
TTrain::SkipPtrValue
int SkipPtrValue
stores the pointer increment from first action in ActionVector for skipped actions when a departure i...
Definition: TrainUnit.h:376
TNumList
std::list< int > TNumList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:91
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:801
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:761
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:43
EnRoute
@ EnRoute
Definition: TrainUnit.h:72
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:3264
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11961
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:831
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:70
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:1002
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:497
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:779
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:885
TUtilities::ModerateDelayFactor
float ModerateDelayFactor
Definition: Utilities.h:54
TUtilities::MajorDelayCutoff
float MajorDelayCutoff
Definition: Utilities.h:52
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:975
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:721
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:356
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
Definition: TrainUnit.h:318
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:1008
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:826
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:9077
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:1011
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
< true if a description is given for the train, if only headcode given for a follow-on service then f...
Definition: TrainUnit.h:210
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:66
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:220
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6822
TTrainController::MTBFHours
double MTBFHours
Mean time between train failures in timetable clock hours.
Definition: TrainUnit.h:818
TTrack::TInfrastructureFailureEntry::TVPos
int TVPos
Definition: TrackUnit.h:715
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:19076
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:476
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:16110
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:807
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:310
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3472
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:1022
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:470
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:5280
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2785
RearSplit
@ RearSplit
Definition: TrainUnit.h:51
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:131
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:53
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:828
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:671
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:972
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:824
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:995
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:135
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:55
TUtilities::FailureMode
TFailureMode FailureMode
specifies whether no failures or minor, moderate or major random failures are to be applied (added at...
Definition: Utilities.h:119
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:3196
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6794
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:16208
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:53
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:894
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:165
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:845
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:869
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:382
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3543
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:968
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:9141
TUtilities::SignalChangeEventsPerFailure
int SignalChangeEventsPerFailure
number of signal changes between failures - reciprocal of failure probability per change
Definition: Utilities.h:95
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:334
TTrack::TInfrastructureFailureEntry
Definition: TrackUnit.h:714
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:8887
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:8166
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:3188
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:830
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:493
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:40
TTrainController::TrainDataVectorCopy
TTrainDataVector TrainDataVectorCopy
vector containing the internal timetable, the copy is used for conflict analysis only
Definition: TrainUnit.h:883
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:466
NewService
@ NewService
Definition: TrainUnit.h:51
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:41
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:16986
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1518
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:488
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:384
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1670
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:372
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:222
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:218
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:837
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:302
TUtilities::CumulativeDelayedRandMinsAllTrains
int CumulativeDelayedRandMinsAllTrains
the running total of all random delays including knock-on delays for all trains, used to reduce total...
Definition: Utilities.h:101
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:170
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:1012
Crossover
@ Crossover
Definition: TrackUnit.h:66
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4880
AtLocation
@ AtLocation
Definition: TrainUnit.h:72
Signal
@ Signal
Definition: TrackUnit.h:76
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:974
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:1005
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:796
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:19732
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
Definition: TrainUnit.h:883
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:20354
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1671
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:468
Exited
@ Exited
Definition: TrainUnit.h:88
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:10938
TTrain::ActionsSkippedFlag
bool ActionsSkippedFlag
prevents any further skipping until after the next departure
Definition: TrainUnit.h:332
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:16684
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:8453
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:19558
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:1024
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2403
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:488
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:59
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1692
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:964
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:488
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:759
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:312
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:803
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:51
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:432
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:815
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:994
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:970
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:368
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:66
TTrain::NewDelay
double NewDelay
an additional random delay at a location (added at v2.13.0)
Definition: TrainUnit.h:444
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:1006
Leave
@ Leave
Definition: TrainUnit.h:51
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:856
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:771
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:799
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel (OperatorA...
Definition: TrainUnit.h:458
TTrack::FailedSignalsVector
TFailedElementVector FailedSignalsVector
Definition: TrackUnit.h:794
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:398
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:3169
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:973
FNil
@ FNil
Definition: Utilities.h:43
NoFormat
@ NoFormat
Definition: TrainUnit.h:66
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:811
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:42
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:42
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:41
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:66
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2714
TTrain::TimeToExit
float TimeToExit
in minutes: new for multiplayer, -1 = > 60 mins
Definition: TrainUnit.h:456
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:488
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3572
Enter
@ Enter
Definition: TrainUnit.h:51
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:518
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:8067
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:980
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:16733
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:450
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:275
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:15388
TActionVectorEntry::NewDescription
AnsiString NewDescription
Definition: TrainUnit.h:121
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:751
TTrain::NewTrainService
void NewTrainService(int Caller, bool NoLogFlag)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6266
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:9577
Timetable
@ Timetable
Definition: TrainUnit.h:60
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:15773
StartSequence
@ StartSequence
Definition: TrainUnit.h:77
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:6881
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:809
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:11812
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:786
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:129
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:19404
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:115
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:801
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:275
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:21323
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:82
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:440
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:769
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:262
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:807
TTrack::InactiveTrackVector
TTrackVector InactiveTrackVector
Definition: TrackUnit.h:828
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, AnsiString SplitDistribution, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5387
Terminate
@ Terminate
Definition: TrainUnit.h:51
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:815
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
< map of coupled continuations
Definition: TrackUnit.h:800
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:488
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:149
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:504
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:370
Running
@ Running
Definition: TrainUnit.h:88
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:284
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3669
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:899
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:14685
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:16246
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:17009
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:460
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:12032
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:728
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:241
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:857
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:426
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2600
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:20415
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:287
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0 for train failures, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:873
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1657
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:10060
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:9630
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6709
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:867
TTrain::GetNewServiceDepartureInfo
AnsiString GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr, bool TimetableTime)
called during FloatingLabelNextString to find the next service departure time & next location (last b...
Definition: TrainUnit.cpp:7505
TTrain::Description
AnsiString Description
needs own HeadCode & description because repeat entries will differ from TrainDataEntry values (Descr...
Definition: TrainUnit.h:326
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:997
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:128
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:520
TUtilities::DelayMode
TDelayMode DelayMode
specifies whether no delays or minor, moderate or major random delays are to be applied (added at v2....
Definition: Utilities.h:117
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:9641
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:1014
TTrain::ExitPair
THVShortPair ExitPair
H & V coordinates of the exit element related to TimeToExit, new for multiplayer.
Definition: TrainUnit.h:474
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1744
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:815
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:12235
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3668
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3368
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:468
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
new v2.2.0 (DistanceToExit added for multiplayer), calcs distances to red signal & exit,...
Definition: TrainUnit.cpp:21636
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:808
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:72
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:53
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1655
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:9034
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:3232
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:119
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:833
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:16855
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:265
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:991
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:16187
TDisplay
Definition: DisplayUnit.h:49
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:147
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:875
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:489
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4817
TimeCmdDescription
@ TimeCmdDescription
Definition: TrainUnit.h:67
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:277
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:38
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in Actions...
Definition: TrainUnit.cpp:10394
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:52
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:358
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:408
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:12335
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:829
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:97
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:9655
TTrackElement::Failed
bool Failed
New parameter added at v2.13.0 for failed points, signals & TSRs.
Definition: TrackUnit.h:141
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:155
TTrainController::SkippedTTEvents
int SkippedTTEvents
Definition: TrainUnit.h:852
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:906
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:448
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:750
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:160
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1725
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:212
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:130
TUtilities::MinorDelayCutoff
float MinorDelayCutoff
Definition: Utilities.h:50
TTimeToExitMultiMapEntry
std::pair< THVShortPair, TExitInfo > TTimeToExitMultiMapEntry
Definition: TrainUnit.h:113
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:996
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:226
TTrain::DelayedRandMins
double DelayedRandMins
the remaining random delay at any point in time for the train (added at v2.13.0)
Definition: TrainUnit.h:442
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:149
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:822
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:910
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:560
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:5303
TTrain::TRSTime
TDateTime TRSTime
Definition: TrainUnit.h:470
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route (this will be truncated...
Definition: TrackUnit.h:1661
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:40
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:107
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1671
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:137
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:338
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:1020
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:714
Moderate
@ Moderate
Definition: Utilities.h:38
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:712
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:301
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:10044
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:897
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:838
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:39
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:354
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:17022
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:770
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:985
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:153
Major
@ Major
Definition: Utilities.h:38
Points
@ Points
Definition: TrackUnit.h:66
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:11740
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:743
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:394
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:832
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:15287
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:40
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:392
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:815
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:855
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:53
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3517
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:1004
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:39
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:402
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:268
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:508
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all signals appropriately. Also called when a new train is added a...
Definition: TrackUnit.cpp:18036
Continuation
@ Continuation
Definition: TrackUnit.h:66
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:801
TTrain::ActualArrivalTime
TDateTime ActualArrivalTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:470
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7825
GraphicUnit.h
PerfLogUnit.h
TUtilities::MajorDelayFactor
float MajorDelayFactor
Definition: Utilities.h:55
TTrack::PointFlashFlag
bool PointFlashFlag
< true if a route set through an LC that is closed to trains (& therefore needs to be opened)
Definition: TrackUnit.h:768
TTrack::ThisLocationLongEnoughForSplit
bool ThisLocationLongEnoughForSplit(int Caller, AnsiString HeadCode, int TrainID, AnsiString LocationName, int LeadElement, int LeadExitPos, int MidElement, int MidEntryPos, int &FrontTrainFrontPos, int &FrontTrainRearPos, int &RearTrainFrontPos, int &RearTrainRearPos, bool &TemporaryDelay)
checks if the track that the train is on is long enough for a split, returns false if not,...
Definition: TrackUnit.cpp:11258
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:848
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:480
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:10811
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:55
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:364
NoEvent
@ NoEvent
Definition: TrainUnit.h:39
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:39
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:20827
TTrack::TInfrastructureFailureEntry::FailureTime
TDateTime FailureTime
Definition: TrackUnit.h:716
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3326
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:902
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:53
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:782
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:905
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:1018
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:10425
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:88
TTrain::FollowOnServiceRef
AnsiString FollowOnServiceRef
Definition: TrainUnit.h:328
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:486
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:681
Minor
@ Minor
Definition: Utilities.h:38
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:742
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6530
TActionVectorEntry::ExitList
TNumList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:135
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:850
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:495
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:269
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:180
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:42
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrainDataEntry::ExplicitDescription
bool ExplicitDescription
< headcode is the first train's headcode, rest are calculated from repeat information; ServiceReferen...
Definition: TrainUnit.h:208
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:515
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:53
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:1001
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:21093
TTrain::RevisedStoppedAtLoc
bool RevisedStoppedAtLoc() const
Definition: TrainUnit.h:521
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:904
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:993
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:314
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:287
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:710
TTrainController::SingleServiceOutput
void SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
Outputs the single service vector for train direction analysis purposes in timetable conflict analysi...
Definition: TrainUnit.cpp:19968
TTrack::TInfrastructureFailureEntry::RepairTime
TDateTime RepairTime
Definition: TrackUnit.h:717
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:723
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:8997
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:65
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:801
TTrain::NextTrainID
static int NextTrainID
< km/h
Definition: TrainUnit.h:323
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:768
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:184
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:726
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:988
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:20189
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:20145
TTrain::CumulativeDelayedRandMinsOneTrain
double CumulativeDelayedRandMinsOneTrain
the running total of all random delays including knock-on delays for a single train,...
Definition: TrainUnit.h:446
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:805
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:121
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:281
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:42
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:40
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
Definition: TrackUnit.h:155
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:909
TTrackElement::StationEntryStopLinkPos4
int StationEntryStopLinkPos4
Used for track at platforms ( 1 & 2) and non-station named locations (1 - 4) to mark the train front ...
Definition: TrackUnit.h:153
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:336
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:50
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:723
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:368
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:842
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:1025
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:82
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:10011
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:16134
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:7721
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:19417
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6587
TTrain::AValue
double AValue
< only true when a train has become a follow-on service early and the follow-on service normally pass...
Definition: TrainUnit.h:418
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:40
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5748
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:20112
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:10029
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:145
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:14692
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:345
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:16815
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:182
TUtilities::MaxRandomRepairTime
int MaxRandomRepairTime
Definition: Utilities.h:71
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:155
NoLocation
@ NoLocation
Definition: TrainUnit.h:72
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool SetDataAndCheckLocations, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:14813
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:936
TTrainController::MinsToAnsiTime
AnsiString MinsToAnsiTime(int Input)
converts an integer minute value to string "HH:MM" added at v2.15.0
Definition: TrainUnit.cpp:16219
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:390
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:39
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:216
NoMode
@ NoMode
Definition: TrainUnit.h:60
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:844
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:12685
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:100
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:772
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:815
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:66
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:173
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:201
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:865
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:836
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:813
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:522
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:488
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:10413
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:984
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:143
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:296
FailEntryRouteSetAgainst
@ FailEntryRouteSetAgainst
Definition: TrainUnit.h:43
TTrain::TrainSkippedEvents
int TrainSkippedEvents
stores the pointer increment from the current action in ActionVector for skipped actions when a depar...
Definition: TrainUnit.h:378
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:784
TExitInfo::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:105
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable - one entry per timetable service entry (the object i...
Definition: TrainUnit.h:239
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:15807
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:294
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7564
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:454
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:710
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1665
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:141
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:360
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:774
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:978
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:1021
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:770
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:3290
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
Definition: TrainUnit.h:121
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:143
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:412
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:105
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:1013
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:121
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6638
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:5012
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
< service description
Definition: TrainUnit.h:743
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:151
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:404
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:861
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:815
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:976
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6588
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:728
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:66
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:52
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:773
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:21277
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
THVShortPair
std::pair< int, int > THVShortPair
Definition: InterfaceUnit.h:82
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:739
TTrainController::TContinuationTrainExpectationEntry::FixedDescription
AnsiString FixedDescription
Definition: TrainUnit.h:741
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:66
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:828
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:1007
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:879
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:725
TActionVectorEntry::SignallerControl
bool SignallerControl
< string values for timetabled event entries, null on creation
Definition: TrainUnit.h:125
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:5292
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:436
TUtilities::LastDelayTTClockTime
double LastDelayTTClockTime
Clock time at which the latest delay for any train occurred. Used to prevent new delays within 5 minu...
Definition: Utilities.h:85
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:151
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:729
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:340
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:756
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:732
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:67
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:841
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:3214
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag = 1, top = 2, top rh diag = 3,...
Definition: TrackUnit.h:90
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:652
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1074
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:998
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:20396
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6558
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
Definition: TrackUnit.h:155
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3710
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:127
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2802
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:303
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:190
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:206
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:6912
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:343
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:41
NotSet
@ NotSet
Definition: TrackUnit.h:76
Repeat
@ Repeat
Definition: TrainUnit.h:67
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:966
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:14701
FinishSequence
@ FinishSequence
Definition: TrainUnit.h:77
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:678
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:75
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:815
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int TrackPos, int LinkPos, int OwnTrainID)
True if another train on LinkPos track of element at TrackPos, whether bridge or not,...
Definition: TrackUnit.cpp:11670
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:161
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:67
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:150
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:489
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:1000
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:750
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:890
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:247
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5904
TAllRoutes::TLockedRouteClass::RearTrackVectorPosition
unsigned int RearTrackVectorPosition
the TrackVector position of the rearmost element selected for truncation (this will be truncated)
Definition: TrackUnit.h:1659
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:20138
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:145
Signaller
@ Signaller
Definition: TrainUnit.h:60
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:501
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:907
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:180
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:39
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:895
TTrain::TreatPassAsTimeLocDeparture
bool TreatPassAsTimeLocDeparture
< indicates failure
Definition: TrainUnit.h:416
Bridge
@ Bridge
Definition: TrackUnit.h:66
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:979
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:279
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:368
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:76
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:428
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:77
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:528
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:37
Buffers
@ Buffers
Definition: TrackUnit.h:66
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:131
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:297
TTrack::OneNonStationLongEnoughForSplit
bool OneNonStationLongEnoughForSplit(int Caller, AnsiString LocationName)
As below but here allow points & crossovers.
Definition: TrackUnit.cpp:11094
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:121